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 test_programs::sockets::supports_ipv6;
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         listener1
60             .bind(IpSocketAddress::new(
61                 IpAddress::new_loopback(ip.family()),
62                 0,
63             ))
64             .unwrap();
65 
66         let bind_addr = listener1.get_local_address().unwrap();
67 
68         // The listener socket must have at least one connection for the TIME_WAIT
69         // mechanism to kick in. So we'll create & accept a dummy connection
70         // before closing the listener:
71         {
72             let mut accept = listener1.listen().unwrap();
73 
74             let connect_addr =
75                 IpSocketAddress::new(IpAddress::new_loopback(ip.family()), bind_addr.port());
76             join!(
77                 async {
78                     client.connect(connect_addr).await.unwrap();
79                 },
80                 async {
81                     let sock = accept.next().await.unwrap();
82                     let (mut data_tx, data_rx) = wit_stream::new();
83                     join!(
84                         async {
85                             sock.send(data_rx).await.unwrap();
86                         },
87                         async {
88                             let remaining = data_tx.write_all(vec![0; 10]).await;
89                             assert!(remaining.is_empty());
90                             drop(data_tx);
91                         }
92                     );
93                 },
94             );
95         }
96 
97         bind_addr
98     };
99 
100     // If SO_REUSEADDR was configured correctly, the following lines
101     // shouldn't be affected by the TIME_WAIT state of the just closed
102     // `listener1` socket:
103     let listener2 = TcpSocket::create(ip.family()).unwrap();
104     listener2.bind(bind_addr).unwrap();
105     listener2.listen().unwrap();
106 }
107 
108 // Try binding to an address that is not configured on the system.
109 fn test_tcp_bind_addrnotavail(ip: IpAddress) {
110     let bind_addr = IpSocketAddress::new(ip, 0);
111 
112     let sock = TcpSocket::create(ip.family()).unwrap();
113 
114     assert_eq!(sock.bind(bind_addr), Err(ErrorCode::AddressNotBindable));
115 }
116 
117 /// Bind should validate the address family.
118 fn test_tcp_bind_wrong_family(family: IpAddressFamily) {
119     let wrong_ip = match family {
120         IpAddressFamily::Ipv4 => IpAddress::IPV6_LOOPBACK,
121         IpAddressFamily::Ipv6 => IpAddress::IPV4_LOOPBACK,
122     };
123 
124     let sock = TcpSocket::create(family).unwrap();
125     let result = sock.bind(IpSocketAddress::new(wrong_ip, 0));
126 
127     assert!(matches!(result, Err(ErrorCode::InvalidArgument)));
128 }
129 
130 /// Bind only works on unicast addresses.
131 fn test_tcp_bind_non_unicast() {
132     let ipv4_broadcast = IpSocketAddress::new(IpAddress::IPV4_BROADCAST, 0);
133     let ipv4_multicast = IpSocketAddress::new(IpAddress::Ipv4((224, 254, 0, 0)), 0);
134     let ipv6_multicast = IpSocketAddress::new(IpAddress::Ipv6((0xff00, 0, 0, 0, 0, 0, 0, 0)), 0);
135 
136     let sock_v4 = TcpSocket::create(IpAddressFamily::Ipv4).unwrap();
137     let sock_v6 = TcpSocket::create(IpAddressFamily::Ipv6).unwrap();
138 
139     assert!(matches!(
140         sock_v4.bind(ipv4_broadcast),
141         Err(ErrorCode::InvalidArgument)
142     ));
143     assert!(matches!(
144         sock_v4.bind(ipv4_multicast),
145         Err(ErrorCode::InvalidArgument)
146     ));
147     assert!(matches!(
148         sock_v6.bind(ipv6_multicast),
149         Err(ErrorCode::InvalidArgument)
150     ));
151 }
152 
153 fn test_tcp_bind_dual_stack() {
154     let sock = TcpSocket::create(IpAddressFamily::Ipv6).unwrap();
155     let addr = IpSocketAddress::new(IpAddress::IPV4_MAPPED_LOOPBACK, 0);
156 
157     // Binding an IPv4-mapped-IPv6 address on a ipv6-only socket should fail:
158     assert!(matches!(sock.bind(addr), Err(ErrorCode::InvalidArgument)));
159 }
160 
161 impl test_programs::p3::exports::wasi::cli::run::Guest for Component {
162     async fn run() -> Result<(), ()> {
163         const RESERVED_IPV4_ADDRESS: IpAddress = IpAddress::Ipv4((192, 0, 2, 0)); // Reserved for documentation and examples.
164         const RESERVED_IPV6_ADDRESS: IpAddress =
165             IpAddress::Ipv6((0x2001, 0x0db8, 0, 0, 0, 0, 0, 0)); // Reserved for documentation and examples.
166 
167         test_tcp_bind_ephemeral_port(IpAddress::IPV4_LOOPBACK);
168         test_tcp_bind_ephemeral_port(IpAddress::IPV4_UNSPECIFIED);
169         test_tcp_bind_specific_port(IpAddress::IPV4_LOOPBACK);
170         test_tcp_bind_specific_port(IpAddress::IPV4_UNSPECIFIED);
171         test_tcp_bind_reuseaddr(IpAddress::IPV4_LOOPBACK).await;
172         test_tcp_bind_addrinuse(IpAddress::IPV4_LOOPBACK);
173         test_tcp_bind_addrinuse(IpAddress::IPV4_UNSPECIFIED);
174         test_tcp_bind_addrnotavail(RESERVED_IPV4_ADDRESS);
175         test_tcp_bind_wrong_family(IpAddressFamily::Ipv4);
176 
177         if supports_ipv6() {
178             test_tcp_bind_ephemeral_port(IpAddress::IPV6_LOOPBACK);
179             test_tcp_bind_ephemeral_port(IpAddress::IPV6_UNSPECIFIED);
180             test_tcp_bind_specific_port(IpAddress::IPV6_LOOPBACK);
181             test_tcp_bind_specific_port(IpAddress::IPV6_UNSPECIFIED);
182             test_tcp_bind_reuseaddr(IpAddress::IPV6_LOOPBACK).await;
183             test_tcp_bind_addrinuse(IpAddress::IPV6_LOOPBACK);
184             test_tcp_bind_addrinuse(IpAddress::IPV6_UNSPECIFIED);
185             test_tcp_bind_addrnotavail(RESERVED_IPV6_ADDRESS);
186             test_tcp_bind_wrong_family(IpAddressFamily::Ipv6);
187             test_tcp_bind_non_unicast();
188             test_tcp_bind_dual_stack();
189         }
190 
191         Ok(())
192     }
193 }
194 
195 fn main() {}
196