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         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     let listener2 = TcpSocket::create(ip.family()).unwrap();
92     listener2.bind(bind_addr).unwrap();
93     listener2.listen().unwrap();
94 }
95 
96 // Try binding to an address that is not configured on the system.
97 fn test_tcp_bind_addrnotavail(ip: IpAddress) {
98     let bind_addr = IpSocketAddress::new(ip, 0);
99 
100     let sock = TcpSocket::create(ip.family()).unwrap();
101 
102     assert_eq!(sock.bind(bind_addr), Err(ErrorCode::AddressNotBindable));
103 }
104 
105 /// Bind should validate the address family.
106 fn test_tcp_bind_wrong_family(family: IpAddressFamily) {
107     let wrong_ip = match family {
108         IpAddressFamily::Ipv4 => IpAddress::IPV6_LOOPBACK,
109         IpAddressFamily::Ipv6 => IpAddress::IPV4_LOOPBACK,
110     };
111 
112     let sock = TcpSocket::create(family).unwrap();
113     let result = sock.bind(IpSocketAddress::new(wrong_ip, 0));
114 
115     assert!(matches!(result, Err(ErrorCode::InvalidArgument)));
116 }
117 
118 /// Bind only works on unicast addresses.
119 fn test_tcp_bind_non_unicast() {
120     let ipv4_broadcast = IpSocketAddress::new(IpAddress::IPV4_BROADCAST, 0);
121     let ipv4_multicast = IpSocketAddress::new(IpAddress::Ipv4((224, 254, 0, 0)), 0);
122     let ipv6_multicast = IpSocketAddress::new(IpAddress::Ipv6((0xff00, 0, 0, 0, 0, 0, 0, 0)), 0);
123 
124     let sock_v4 = TcpSocket::create(IpAddressFamily::Ipv4).unwrap();
125     let sock_v6 = TcpSocket::create(IpAddressFamily::Ipv6).unwrap();
126 
127     assert!(matches!(
128         sock_v4.bind(ipv4_broadcast),
129         Err(ErrorCode::InvalidArgument)
130     ));
131     assert!(matches!(
132         sock_v4.bind(ipv4_multicast),
133         Err(ErrorCode::InvalidArgument)
134     ));
135     assert!(matches!(
136         sock_v6.bind(ipv6_multicast),
137         Err(ErrorCode::InvalidArgument)
138     ));
139 }
140 
141 fn test_tcp_bind_dual_stack() {
142     let sock = TcpSocket::create(IpAddressFamily::Ipv6).unwrap();
143     let addr = IpSocketAddress::new(IpAddress::IPV4_MAPPED_LOOPBACK, 0);
144 
145     // Binding an IPv4-mapped-IPv6 address on a ipv6-only socket should fail:
146     assert!(matches!(sock.bind(addr), Err(ErrorCode::InvalidArgument)));
147 }
148 
149 impl test_programs::p3::exports::wasi::cli::run::Guest for Component {
150     async fn run() -> Result<(), ()> {
151         const RESERVED_IPV4_ADDRESS: IpAddress = IpAddress::Ipv4((192, 0, 2, 0)); // Reserved for documentation and examples.
152         const RESERVED_IPV6_ADDRESS: IpAddress =
153             IpAddress::Ipv6((0x2001, 0x0db8, 0, 0, 0, 0, 0, 0)); // Reserved for documentation and examples.
154 
155         test_tcp_bind_ephemeral_port(IpAddress::IPV4_LOOPBACK);
156         test_tcp_bind_ephemeral_port(IpAddress::IPV4_UNSPECIFIED);
157         test_tcp_bind_specific_port(IpAddress::IPV4_LOOPBACK);
158         test_tcp_bind_specific_port(IpAddress::IPV4_UNSPECIFIED);
159         test_tcp_bind_reuseaddr(IpAddress::IPV4_LOOPBACK).await;
160         test_tcp_bind_addrinuse(IpAddress::IPV4_LOOPBACK);
161         test_tcp_bind_addrinuse(IpAddress::IPV4_UNSPECIFIED);
162         test_tcp_bind_addrnotavail(RESERVED_IPV4_ADDRESS);
163         test_tcp_bind_wrong_family(IpAddressFamily::Ipv4);
164 
165         if supports_ipv6() {
166             test_tcp_bind_ephemeral_port(IpAddress::IPV6_LOOPBACK);
167             test_tcp_bind_ephemeral_port(IpAddress::IPV6_UNSPECIFIED);
168             test_tcp_bind_specific_port(IpAddress::IPV6_LOOPBACK);
169             test_tcp_bind_specific_port(IpAddress::IPV6_UNSPECIFIED);
170             test_tcp_bind_reuseaddr(IpAddress::IPV6_LOOPBACK).await;
171             test_tcp_bind_addrinuse(IpAddress::IPV6_LOOPBACK);
172             test_tcp_bind_addrinuse(IpAddress::IPV6_UNSPECIFIED);
173             test_tcp_bind_addrnotavail(RESERVED_IPV6_ADDRESS);
174             test_tcp_bind_wrong_family(IpAddressFamily::Ipv6);
175             test_tcp_bind_non_unicast();
176             test_tcp_bind_dual_stack();
177         }
178 
179         Ok(())
180     }
181 }
182 
183 fn main() {}
184