1 use futures::join;
2 use test_programs::p3::sockets::attempt_random_port;
3 use test_programs::p3::wasi::sockets::types::{
4     ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, TcpSocket,
5 };
6 use test_programs::p3::wit_stream;
7 use wit_bindgen::yield_blocking;
8 
9 struct Component;
10 
11 test_programs::p3::export!(Component);
12 
13 /// Bind a socket and let the system determine a port.
14 fn test_tcp_bind_ephemeral_port(ip: IpAddress) {
15     let bind_addr = IpSocketAddress::new(ip, 0);
16 
17     let sock = TcpSocket::create(ip.family()).unwrap();
18     sock.bind(bind_addr).unwrap();
19 
20     let bound_addr = sock.get_local_address().unwrap();
21 
22     assert_eq!(bind_addr.ip(), bound_addr.ip());
23     assert_ne!(bind_addr.port(), bound_addr.port());
24 }
25 
26 /// Bind a socket on a specified port.
27 fn test_tcp_bind_specific_port(ip: IpAddress) {
28     let sock = TcpSocket::create(ip.family()).unwrap();
29 
30     let bind_addr = attempt_random_port(ip, |bind_addr| sock.bind(bind_addr)).unwrap();
31 
32     let bound_addr = sock.get_local_address().unwrap();
33 
34     assert_eq!(bind_addr.ip(), bound_addr.ip());
35     assert_eq!(bind_addr.port(), bound_addr.port());
36 }
37 
38 /// Two sockets may not be actively bound to the same address at the same time.
39 fn test_tcp_bind_addrinuse(ip: IpAddress) {
40     let bind_addr = IpSocketAddress::new(ip, 0);
41 
42     let sock1 = TcpSocket::create(ip.family()).unwrap();
43     sock1.bind(bind_addr).unwrap();
44     sock1.listen().unwrap();
45 
46     let bound_addr = sock1.get_local_address().unwrap();
47 
48     let sock2 = TcpSocket::create(ip.family()).unwrap();
49     assert_eq!(sock2.bind(bound_addr), Err(ErrorCode::AddressInUse));
50 }
51 
52 // The WASI runtime should set SO_REUSEADDR for us
53 async fn test_tcp_bind_reuseaddr(ip: IpAddress) {
54     let client = TcpSocket::create(ip.family()).unwrap();
55 
56     let bind_addr = {
57         let listener1 = TcpSocket::create(ip.family()).unwrap();
58 
59         let bind_addr = attempt_random_port(ip, |bind_addr| listener1.bind(bind_addr)).unwrap();
60 
61         let mut accept = listener1.listen().unwrap();
62 
63         let connect_addr =
64             IpSocketAddress::new(IpAddress::new_loopback(ip.family()), bind_addr.port());
65         join!(
66             async {
67                 client.connect(connect_addr).await.unwrap();
68             },
69             async {
70                 let sock = accept.next().await.unwrap();
71                 let (mut data_tx, data_rx) = wit_stream::new();
72                 join!(
73                     async {
74                         sock.send(data_rx).await.unwrap();
75                     },
76                     async {
77                         let remaining = data_tx.write_all(vec![0; 10]).await;
78                         assert!(remaining.is_empty());
79                         drop(data_tx);
80                     }
81                 );
82             },
83         );
84 
85         bind_addr
86     };
87 
88     // If SO_REUSEADDR was configured correctly, the following lines
89     // shouldn't be affected by the TIME_WAIT state of the just closed
90     // `listener1` socket.
91     //
92     // Note though that the way things are modeled in Wasmtime right now is that
93     // the TCP socket is kept alive by a spawned task created in `listen`
94     // meaning that to fully close the socket it requires the spawned task to
95     // shut down. That may require yielding to the host or similar so try a few
96     // times to let the host get around to closing the task while testing each
97     // time to see if we can reuse the address. This loop is bounded because it
98     // should complete "quickly".
99     for _ in 0..10 {
100         let listener2 = TcpSocket::create(ip.family()).unwrap();
101         if listener2.bind(bind_addr).is_ok() {
102             listener2.listen().unwrap();
103             return;
104         }
105         yield_blocking();
106     }
107 
108     panic!("looks like REUSEADDR isn't in use?");
109 }
110 
111 // Try binding to an address that is not configured on the system.
112 fn test_tcp_bind_addrnotavail(ip: IpAddress) {
113     let bind_addr = IpSocketAddress::new(ip, 0);
114 
115     let sock = TcpSocket::create(ip.family()).unwrap();
116 
117     assert_eq!(sock.bind(bind_addr), Err(ErrorCode::AddressNotBindable));
118 }
119 
120 /// Bind should validate the address family.
121 fn test_tcp_bind_wrong_family(family: IpAddressFamily) {
122     let wrong_ip = match family {
123         IpAddressFamily::Ipv4 => IpAddress::IPV6_LOOPBACK,
124         IpAddressFamily::Ipv6 => IpAddress::IPV4_LOOPBACK,
125     };
126 
127     let sock = TcpSocket::create(family).unwrap();
128     let result = sock.bind(IpSocketAddress::new(wrong_ip, 0));
129 
130     assert!(matches!(result, Err(ErrorCode::InvalidArgument)));
131 }
132 
133 /// Bind only works on unicast addresses.
134 fn test_tcp_bind_non_unicast() {
135     let ipv4_broadcast = IpSocketAddress::new(IpAddress::IPV4_BROADCAST, 0);
136     let ipv4_multicast = IpSocketAddress::new(IpAddress::Ipv4((224, 254, 0, 0)), 0);
137     let ipv6_multicast = IpSocketAddress::new(IpAddress::Ipv6((0xff00, 0, 0, 0, 0, 0, 0, 0)), 0);
138 
139     let sock_v4 = TcpSocket::create(IpAddressFamily::Ipv4).unwrap();
140     let sock_v6 = TcpSocket::create(IpAddressFamily::Ipv6).unwrap();
141 
142     assert!(matches!(
143         sock_v4.bind(ipv4_broadcast),
144         Err(ErrorCode::InvalidArgument)
145     ));
146     assert!(matches!(
147         sock_v4.bind(ipv4_multicast),
148         Err(ErrorCode::InvalidArgument)
149     ));
150     assert!(matches!(
151         sock_v6.bind(ipv6_multicast),
152         Err(ErrorCode::InvalidArgument)
153     ));
154 }
155 
156 fn test_tcp_bind_dual_stack() {
157     let sock = TcpSocket::create(IpAddressFamily::Ipv6).unwrap();
158     let addr = IpSocketAddress::new(IpAddress::IPV4_MAPPED_LOOPBACK, 0);
159 
160     // Binding an IPv4-mapped-IPv6 address on a ipv6-only socket should fail:
161     assert!(matches!(sock.bind(addr), Err(ErrorCode::InvalidArgument)));
162 }
163 
164 impl test_programs::p3::exports::wasi::cli::run::Guest for Component {
165     async fn run() -> Result<(), ()> {
166         const RESERVED_IPV4_ADDRESS: IpAddress = IpAddress::Ipv4((192, 0, 2, 0)); // Reserved for documentation and examples.
167         const RESERVED_IPV6_ADDRESS: IpAddress =
168             IpAddress::Ipv6((0x2001, 0x0db8, 0, 0, 0, 0, 0, 0)); // Reserved for documentation and examples.
169 
170         test_tcp_bind_ephemeral_port(IpAddress::IPV4_LOOPBACK);
171         test_tcp_bind_ephemeral_port(IpAddress::IPV6_LOOPBACK);
172         test_tcp_bind_ephemeral_port(IpAddress::IPV4_UNSPECIFIED);
173         test_tcp_bind_ephemeral_port(IpAddress::IPV6_UNSPECIFIED);
174 
175         test_tcp_bind_specific_port(IpAddress::IPV4_LOOPBACK);
176         test_tcp_bind_specific_port(IpAddress::IPV6_LOOPBACK);
177         test_tcp_bind_specific_port(IpAddress::IPV4_UNSPECIFIED);
178         test_tcp_bind_specific_port(IpAddress::IPV6_UNSPECIFIED);
179 
180         test_tcp_bind_reuseaddr(IpAddress::IPV4_LOOPBACK).await;
181         test_tcp_bind_reuseaddr(IpAddress::IPV6_LOOPBACK).await;
182 
183         test_tcp_bind_addrinuse(IpAddress::IPV4_LOOPBACK);
184         test_tcp_bind_addrinuse(IpAddress::IPV6_LOOPBACK);
185         test_tcp_bind_addrinuse(IpAddress::IPV4_UNSPECIFIED);
186         test_tcp_bind_addrinuse(IpAddress::IPV6_UNSPECIFIED);
187 
188         test_tcp_bind_addrnotavail(RESERVED_IPV4_ADDRESS);
189         test_tcp_bind_addrnotavail(RESERVED_IPV6_ADDRESS);
190 
191         test_tcp_bind_wrong_family(IpAddressFamily::Ipv4);
192         test_tcp_bind_wrong_family(IpAddressFamily::Ipv6);
193 
194         test_tcp_bind_non_unicast();
195 
196         test_tcp_bind_dual_stack();
197 
198         Ok(())
199     }
200 }
201 
202 fn main() {}
203