Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Udp #2

Closed
wants to merge 46 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
8fcb76a
Use Rustix::Errno to unify error code mapping.
badeend Sep 21, 2023
a620fd0
Clarify Connect failure state
badeend Sep 21, 2023
17c8b8e
Allow accept() to return transient errors.
badeend Sep 21, 2023
3aa2bbb
Fold `ephemeral-ports-exhausted` into `address-in-use`
badeend Sep 21, 2023
6158478
Clarify `local-address` behavior on unbound socket
badeend Sep 21, 2023
184ae30
Remove `concurrency-conflict` clutter,
badeend Sep 21, 2023
72ec5e4
Simplify state errors.
badeend Sep 23, 2023
24a3712
Document that connect may return ECONNABORTED
badeend Sep 23, 2023
70c79ff
Remove create-tcp/udp-socket not supported errors.
badeend Sep 26, 2023
553a9ec
Simplify "not supported" and "invalid argument" error cases.
badeend Sep 26, 2023
86cb6e9
Move example_body out of lib.rs into its own test-case make room for …
badeend Sep 26, 2023
eabf480
Refactor TCP integration tests:
badeend Sep 26, 2023
3a76e8a
Fix get/set_unicast_hop_limit on Linux
badeend Sep 28, 2023
f6ea079
Test TCP socket states
badeend Sep 28, 2023
ce8577c
Keep track of address family ourselves.
badeend Sep 30, 2023
bb92b9f
Add more tests and make it work on Linux
badeend Sep 30, 2023
4fc3770
Fix Windows
badeend Sep 30, 2023
d764d62
Simplify integration tests.
badeend Sep 30, 2023
9c0df8a
Test ipv6_only inheritence
badeend Sep 30, 2023
8726a0e
Merge branch 'main' of https://github.com/bytecodealliance/wasmtime i…
badeend Sep 30, 2023
3b81ec2
Test that socket options keep being respected, even if listen() has a…
badeend Sep 30, 2023
0901e19
cargo fmt
badeend Oct 1, 2023
54bee5d
Duplicate .wit changes to wasi-http
badeend Oct 1, 2023
fd91898
prtest:full
badeend Oct 2, 2023
21b1b8d
Fix BSD behavior of SO_SNDBUF/SO_RCVBUF
badeend Oct 4, 2023
7c2e300
Merge branch 'main' of https://github.com/bytecodealliance/wasmtime i…
badeend Oct 4, 2023
ceb0319
fmt
badeend Oct 4, 2023
d92099d
Fix type error
badeend Oct 5, 2023
9b5886c
Got lost during merge
badeend Oct 5, 2023
5a2da6e
Implement listen backlog tests
badeend Oct 5, 2023
cba24c0
Manually inherit buffer size from listener on MacOS.
badeend Oct 5, 2023
565583d
Keep track of IPV6_V6ONLY ourselves.
badeend Oct 7, 2023
3767e18
Reject IPv4-compatible IPv6 addresses.
badeend Oct 7, 2023
0e41f74
feat(wasi-sockets): implement UDP
rvolosatovs Oct 4, 2023
62e5d7a
refactor(wasi-sockets): simplify UDP implementation
rvolosatovs Oct 6, 2023
4dd48e7
feat(wasi-sockets): store UDP connect address in state
rvolosatovs Oct 6, 2023
201c8b4
fix(wasi-sockets): avoid `shutdown` on `drop`
rvolosatovs Oct 6, 2023
fc82757
Merge branch 'main' of https://github.com/bytecodealliance/wasmtime i…
badeend Oct 8, 2023
da243be
Remove intermediate SystemError trait
badeend Oct 8, 2023
67fae1a
Fix ambiguous .into()'s
badeend Oct 8, 2023
d00e12a
Fix IPV6_UNICAST_HOPS inheritance on MacOS
badeend Oct 8, 2023
df958fe
Remove explicit bind
badeend Oct 8, 2023
412347e
Simplify start_connect&finish_connect.
badeend Oct 8, 2023
f85cb73
Merge branch 'feat/preview2_udp' of https://github.com/rvolosatovs/wa…
badeend Oct 8, 2023
717a5dd
Move UDP test to single file, similar to `tcp_sample_application.rs`
badeend Oct 8, 2023
af648c1
Update UDP tests
badeend Oct 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions crates/test-programs/tests/wasi-sockets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,33 @@ async fn run(name: &str) -> anyhow::Result<()> {
}

#[test_log::test(tokio::test(flavor = "multi_thread"))]
async fn tcp_v4() {
run("tcp_v4").await.unwrap();
async fn tcp_sample_application() {
run("tcp_sample_application").await.unwrap();
}

#[test_log::test(tokio::test(flavor = "multi_thread"))]
async fn tcp_v6() {
run("tcp_v6").await.unwrap();
async fn tcp_bind() {
run("tcp_bind").await.unwrap();
}

#[test_log::test(tokio::test(flavor = "multi_thread"))]
async fn tcp_connect() {
run("tcp_connect").await.unwrap();
}

#[test_log::test(tokio::test(flavor = "multi_thread"))]
async fn tcp_states() {
run("tcp_states").await.unwrap();
}

#[test_log::test(tokio::test(flavor = "multi_thread"))]
async fn tcp_sockopts() {
run("tcp_sockopts").await.unwrap();
}

#[test_log::test(tokio::test(flavor = "multi_thread"))]
async fn udp_sample_application() {
run("udp_sample_application").await.unwrap();
}

#[test_log::test(tokio::test(flavor = "multi_thread"))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ fn main() {
assert!(addresses.resolve_next_address().is_ok());

let result = ip_name_lookup::resolve_addresses(&network, "a.b<&>", None, false);
assert!(matches!(result, Err(network::ErrorCode::InvalidName)));
assert!(matches!(result, Err(network::ErrorCode::InvalidArgument)));

// Try resolving a valid address and ensure that it eventually terminates.
// To help prevent this test from being flaky this additionally times out
Expand Down
151 changes: 151 additions & 0 deletions crates/test-programs/wasi-sockets-tests/src/bin/tcp_bind.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
use wasi::sockets::network::{ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Network};
use wasi::sockets::tcp::TcpSocket;
use wasi_sockets_tests::*;

/// Bind a socket and let the system determine a port.
fn test_tcp_bind_ephemeral_port(net: &Network, ip: IpAddress) {
let bind_addr = IpSocketAddress::new(ip, 0);

let sock = TcpSocket::new(ip.family()).unwrap();
sock.blocking_bind(net, bind_addr).unwrap();

let bound_addr = sock.local_address().unwrap();

assert_eq!(bind_addr.ip(), bound_addr.ip());
assert_ne!(bind_addr.port(), bound_addr.port());
}

/// Bind a socket on a specified port.
fn test_tcp_bind_specific_port(net: &Network, ip: IpAddress) {
const PORT: u16 = 54321;

let bind_addr = IpSocketAddress::new(ip, PORT);

let sock = TcpSocket::new(ip.family()).unwrap();
sock.blocking_bind(net, bind_addr).unwrap();

let bound_addr = sock.local_address().unwrap();

assert_eq!(bind_addr.ip(), bound_addr.ip());
assert_eq!(bind_addr.port(), bound_addr.port());
}

/// Two sockets may not be actively bound to the same address at the same time.
fn test_tcp_bind_addrinuse(net: &Network, ip: IpAddress) {
let bind_addr = IpSocketAddress::new(ip, 0);

let sock1 = TcpSocket::new(ip.family()).unwrap();
sock1.blocking_bind(net, bind_addr).unwrap();
sock1.blocking_listen().unwrap();

let bound_addr = sock1.local_address().unwrap();

let sock2 = TcpSocket::new(ip.family()).unwrap();
assert_eq!(
sock2.blocking_bind(net, bound_addr),
Err(ErrorCode::AddressInUse)
);
}

// Try binding to an address that is not configured on the system.
fn test_tcp_bind_addrnotavail(net: &Network, ip: IpAddress) {
let bind_addr = IpSocketAddress::new(ip, 0);

let sock = TcpSocket::new(ip.family()).unwrap();

assert_eq!(
sock.blocking_bind(net, bind_addr),
Err(ErrorCode::AddressNotBindable)
);
}

/// Bind should validate the address family.
fn test_tcp_bind_wrong_family(net: &Network, family: IpAddressFamily) {
let wrong_ip = match family {
IpAddressFamily::Ipv4 => IpAddress::IPV6_LOOPBACK,
IpAddressFamily::Ipv6 => IpAddress::IPV4_LOOPBACK,
};

let sock = TcpSocket::new(family).unwrap();
let result = sock.blocking_bind(net, IpSocketAddress::new(wrong_ip, 0));

assert!(matches!(result, Err(ErrorCode::InvalidArgument)));
}

/// Bind only works on unicast addresses.
fn test_tcp_bind_non_unicast(net: &Network) {
let ipv4_broadcast = IpSocketAddress::new(IpAddress::IPV4_BROADCAST, 0);
let ipv4_multicast = IpSocketAddress::new(IpAddress::Ipv4((224, 254, 0, 0)), 0);
let ipv6_multicast = IpSocketAddress::new(IpAddress::Ipv6((0xff00, 0, 0, 0, 0, 0, 0, 0)), 0);

let sock_v4 = TcpSocket::new(IpAddressFamily::Ipv4).unwrap();
let sock_v6 = TcpSocket::new(IpAddressFamily::Ipv6).unwrap();

assert!(matches!(
sock_v4.blocking_bind(net, ipv4_broadcast),
Err(ErrorCode::InvalidArgument)
));
assert!(matches!(
sock_v4.blocking_bind(net, ipv4_multicast),
Err(ErrorCode::InvalidArgument)
));
assert!(matches!(
sock_v6.blocking_bind(net, ipv6_multicast),
Err(ErrorCode::InvalidArgument)
));
}

fn test_tcp_bind_dual_stack(net: &Network) {
let sock = TcpSocket::new(IpAddressFamily::Ipv6).unwrap();
let addr = IpSocketAddress::new(IpAddress::IPV4_MAPPED_LOOPBACK, 0);

// Even on platforms that don't support dualstack sockets,
// setting ipv6_only to true (disabling dualstack mode) should work.
sock.set_ipv6_only(true).unwrap();

// Binding an IPv4-mapped-IPv6 address on a ipv6-only socket should fail:
assert!(matches!(
sock.blocking_bind(net, addr),
Err(ErrorCode::InvalidArgument)
));

sock.set_ipv6_only(false).unwrap();

sock.blocking_bind(net, addr).unwrap();

let bound_addr = sock.local_address().unwrap();

assert_eq!(bound_addr.family(), IpAddressFamily::Ipv6);
}

fn main() {
const RESERVED_IPV4_ADDRESS: IpAddress = IpAddress::Ipv4((192, 0, 2, 0)); // Reserved for documentation and examples.
const RESERVED_IPV6_ADDRESS: IpAddress = IpAddress::Ipv6((0x2001, 0x0db8, 0, 0, 0, 0, 0, 0)); // Reserved for documentation and examples.

let net = Network::default();

test_tcp_bind_ephemeral_port(&net, IpAddress::IPV4_LOOPBACK);
test_tcp_bind_ephemeral_port(&net, IpAddress::IPV6_LOOPBACK);
test_tcp_bind_ephemeral_port(&net, IpAddress::IPV4_UNSPECIFIED);
test_tcp_bind_ephemeral_port(&net, IpAddress::IPV6_UNSPECIFIED);

test_tcp_bind_specific_port(&net, IpAddress::IPV4_LOOPBACK);
test_tcp_bind_specific_port(&net, IpAddress::IPV6_LOOPBACK);
test_tcp_bind_specific_port(&net, IpAddress::IPV4_UNSPECIFIED);
test_tcp_bind_specific_port(&net, IpAddress::IPV6_UNSPECIFIED);

test_tcp_bind_addrinuse(&net, IpAddress::IPV4_LOOPBACK);
test_tcp_bind_addrinuse(&net, IpAddress::IPV6_LOOPBACK);
test_tcp_bind_addrinuse(&net, IpAddress::IPV4_UNSPECIFIED);
test_tcp_bind_addrinuse(&net, IpAddress::IPV6_UNSPECIFIED);

test_tcp_bind_addrnotavail(&net, RESERVED_IPV4_ADDRESS);
test_tcp_bind_addrnotavail(&net, RESERVED_IPV6_ADDRESS);

test_tcp_bind_wrong_family(&net, IpAddressFamily::Ipv4);
test_tcp_bind_wrong_family(&net, IpAddressFamily::Ipv6);

test_tcp_bind_non_unicast(&net);

test_tcp_bind_dual_stack(&net);
}
119 changes: 119 additions & 0 deletions crates/test-programs/wasi-sockets-tests/src/bin/tcp_connect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use wasi::sockets::network::{ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Network};
use wasi::sockets::tcp::TcpSocket;
use wasi_sockets_tests::*;

const SOME_PORT: u16 = 47; // If the tests pass, this will never actually be connected to.

/// `0.0.0.0` / `::` is not a valid remote address in WASI.
fn test_tcp_connect_unspec(net: &Network, family: IpAddressFamily) {
let addr = IpSocketAddress::new(IpAddress::new_unspecified(family), SOME_PORT);
let sock = TcpSocket::new(family).unwrap();

assert!(matches!(
sock.blocking_connect(net, addr),
Err(ErrorCode::InvalidArgument)
));
}

/// 0 is not a valid remote port.
fn test_tcp_connect_port_0(net: &Network, family: IpAddressFamily) {
let addr = IpSocketAddress::new(IpAddress::new_loopback(family), 0);
let sock = TcpSocket::new(family).unwrap();

assert!(matches!(
sock.blocking_connect(net, addr),
Err(ErrorCode::InvalidArgument)
));
}

/// Bind should validate the address family.
fn test_tcp_connect_wrong_family(net: &Network, family: IpAddressFamily) {
let wrong_ip = match family {
IpAddressFamily::Ipv4 => IpAddress::IPV6_LOOPBACK,
IpAddressFamily::Ipv6 => IpAddress::IPV4_LOOPBACK,
};
let remote_addr = IpSocketAddress::new(wrong_ip, SOME_PORT);

let sock = TcpSocket::new(family).unwrap();

assert!(matches!(
sock.blocking_connect(net, remote_addr),
Err(ErrorCode::InvalidArgument)
));
}

/// Can only connect to unicast addresses.
fn test_tcp_connect_non_unicast(net: &Network) {
let ipv4_broadcast = IpSocketAddress::new(IpAddress::IPV4_BROADCAST, SOME_PORT);
let ipv4_multicast = IpSocketAddress::new(IpAddress::Ipv4((224, 254, 0, 0)), SOME_PORT);
let ipv6_multicast =
IpSocketAddress::new(IpAddress::Ipv6((0xff00, 0, 0, 0, 0, 0, 0, 0)), SOME_PORT);

let sock_v4 = TcpSocket::new(IpAddressFamily::Ipv4).unwrap();
let sock_v6 = TcpSocket::new(IpAddressFamily::Ipv6).unwrap();

assert!(matches!(
sock_v4.blocking_connect(net, ipv4_broadcast),
Err(ErrorCode::InvalidArgument)
));
assert!(matches!(
sock_v4.blocking_connect(net, ipv4_multicast),
Err(ErrorCode::InvalidArgument)
));
assert!(matches!(
sock_v6.blocking_connect(net, ipv6_multicast),
Err(ErrorCode::InvalidArgument)
));
}

fn test_tcp_connect_dual_stack(net: &Network) {
// Set-up:
let v4_listener = TcpSocket::new(IpAddressFamily::Ipv4).unwrap();
v4_listener
.blocking_bind(&net, IpSocketAddress::new(IpAddress::IPV4_LOOPBACK, 0))
.unwrap();
v4_listener.blocking_listen().unwrap();

let v4_listener_addr = v4_listener.local_address().unwrap();
let v6_listener_addr =
IpSocketAddress::new(IpAddress::IPV4_MAPPED_LOOPBACK, v4_listener_addr.port());

let v6_client = TcpSocket::new(IpAddressFamily::Ipv6).unwrap();

// Tests:

// Even on platforms that don't support dualstack sockets,
// setting ipv6_only to true (disabling dualstack mode) should work.
v6_client.set_ipv6_only(true).unwrap();

// Connecting to an IPv4-mapped-IPv6 address on an ipv6-only socket should fail:
assert!(matches!(
v6_client.blocking_connect(net, v6_listener_addr),
Err(ErrorCode::InvalidArgument)
));

v6_client.set_ipv6_only(false).unwrap();

v6_client.blocking_connect(net, v6_listener_addr).unwrap();

let connected_addr = v6_client.local_address().unwrap();

assert_eq!(connected_addr.family(), IpAddressFamily::Ipv6);
}

fn main() {
let net = Network::default();

test_tcp_connect_unspec(&net, IpAddressFamily::Ipv4);
test_tcp_connect_unspec(&net, IpAddressFamily::Ipv6);

test_tcp_connect_port_0(&net, IpAddressFamily::Ipv4);
test_tcp_connect_port_0(&net, IpAddressFamily::Ipv6);

test_tcp_connect_wrong_family(&net, IpAddressFamily::Ipv4);
test_tcp_connect_wrong_family(&net, IpAddressFamily::Ipv6);

test_tcp_connect_non_unicast(&net);

test_tcp_connect_dual_stack(&net);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use wasi::sockets::network::{
IpAddressFamily, IpSocketAddress, Ipv4SocketAddress, Ipv6SocketAddress, Network,
};
use wasi::sockets::tcp::TcpSocket;
use wasi_sockets_tests::*;

fn test_sample_application(family: IpAddressFamily, bind_address: IpSocketAddress) {
let first_message = b"Hello, world!";
let second_message = b"Greetings, planet!";

let net = Network::default();
let listener = TcpSocket::new(family).unwrap();

listener.blocking_bind(&net, bind_address).unwrap();
listener.set_listen_backlog_size(32).unwrap();
listener.blocking_listen().unwrap();

let addr = listener.local_address().unwrap();

{
let client = TcpSocket::new(family).unwrap();
let (_client_input, client_output) = client.blocking_connect(&net, addr).unwrap();

client_output.blocking_write_util(&[]).unwrap();
client_output.blocking_write_util(first_message).unwrap();
}

{
let (_accepted, input, _output) = listener.accept().unwrap();

let empty_data = input.read(0).unwrap();
assert!(empty_data.is_empty());

let data = input.blocking_read(first_message.len() as u64).unwrap();

// Check that we sent and recieved our message!
assert_eq!(data, first_message); // Not guaranteed to work but should work in practice.
}

// Another client
{
let client = TcpSocket::new(family).unwrap();
let (_client_input, client_output) = client.blocking_connect(&net, addr).unwrap();

client_output.blocking_write_util(second_message).unwrap();
}

{
let (_accepted, input, _output) = listener.accept().unwrap();
let data = input.blocking_read(second_message.len() as u64).unwrap();

// Check that we sent and recieved our message!
assert_eq!(data, second_message); // Not guaranteed to work but should work in practice.
}
}

fn main() {
test_sample_application(
IpAddressFamily::Ipv4,
IpSocketAddress::Ipv4(Ipv4SocketAddress {
port: 0, // use any free port
address: (127, 0, 0, 1), // localhost
}),
);
test_sample_application(
IpAddressFamily::Ipv6,
IpSocketAddress::Ipv6(Ipv6SocketAddress {
port: 0, // use any free port
address: (0, 0, 0, 0, 0, 0, 0, 1), // localhost
flow_info: 0,
scope_id: 0,
}),
);
}
Loading