1 use test_programs::sockets::attempt_random_port;
2 use test_programs::sockets::supports_ipv6;
3 use test_programs::wasi::sockets::network::{
4     ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Network,
5 };
6 use test_programs::wasi::sockets::udp::UdpSocket;
7 
8 /// Bind a socket and let the system determine a port.
9 fn test_udp_bind_ephemeral_port(net: &Network, ip: IpAddress) {
10     let bind_addr = IpSocketAddress::new(ip, 0);
11 
12     let sock = UdpSocket::new(ip.family()).unwrap();
13     sock.blocking_bind(net, bind_addr).unwrap();
14 
15     let bound_addr = sock.local_address().unwrap();
16 
17     assert_eq!(bind_addr.ip(), bound_addr.ip());
18     assert_ne!(bind_addr.port(), bound_addr.port());
19 }
20 
21 /// Bind a socket on a specified port.
22 fn test_udp_bind_specific_port(net: &Network, ip: IpAddress) {
23     let sock = UdpSocket::new(ip.family()).unwrap();
24 
25     let bind_addr =
26         attempt_random_port(ip, |bind_addr| sock.blocking_bind(net, bind_addr)).unwrap();
27 
28     let bound_addr = sock.local_address().unwrap();
29 
30     assert_eq!(bind_addr.ip(), bound_addr.ip());
31     assert_eq!(bind_addr.port(), bound_addr.port());
32 }
33 
34 /// Two sockets may not be actively bound to the same address at the same time.
35 fn test_udp_bind_addrinuse(net: &Network, ip: IpAddress) {
36     let bind_addr = IpSocketAddress::new(ip, 0);
37 
38     let sock1 = UdpSocket::new(ip.family()).unwrap();
39     sock1.blocking_bind(net, bind_addr).unwrap();
40 
41     let bound_addr = sock1.local_address().unwrap();
42 
43     let sock2 = UdpSocket::new(ip.family()).unwrap();
44     assert!(matches!(
45         sock2.blocking_bind(net, bound_addr),
46         Err(ErrorCode::AddressInUse)
47     ));
48 }
49 
50 // Try binding to an address that is not configured on the system.
51 fn test_udp_bind_addrnotavail(net: &Network, ip: IpAddress) {
52     let bind_addr = IpSocketAddress::new(ip, 0);
53 
54     let sock = UdpSocket::new(ip.family()).unwrap();
55 
56     assert!(matches!(
57         sock.blocking_bind(net, bind_addr),
58         Err(ErrorCode::AddressNotBindable)
59     ));
60 }
61 
62 /// Bind should validate the address family.
63 fn test_udp_bind_wrong_family(net: &Network, family: IpAddressFamily) {
64     let wrong_ip = match family {
65         IpAddressFamily::Ipv4 => IpAddress::IPV6_LOOPBACK,
66         IpAddressFamily::Ipv6 => IpAddress::IPV4_LOOPBACK,
67     };
68 
69     let sock = UdpSocket::new(family).unwrap();
70     let result = sock.blocking_bind(net, IpSocketAddress::new(wrong_ip, 0));
71 
72     assert!(matches!(result, Err(ErrorCode::InvalidArgument)));
73 }
74 
75 fn test_udp_bind_dual_stack(net: &Network) {
76     let sock = UdpSocket::new(IpAddressFamily::Ipv6).unwrap();
77     let addr = IpSocketAddress::new(IpAddress::IPV4_MAPPED_LOOPBACK, 0);
78 
79     // Binding an IPv4-mapped-IPv6 address on a ipv6-only socket should fail:
80     assert!(matches!(
81         sock.blocking_bind(net, addr),
82         Err(ErrorCode::InvalidArgument)
83     ));
84 }
85 
86 fn main() {
87     const RESERVED_IPV4_ADDRESS: IpAddress = IpAddress::Ipv4((192, 0, 2, 0)); // Reserved for documentation and examples.
88     const RESERVED_IPV6_ADDRESS: IpAddress = IpAddress::Ipv6((0x2001, 0x0db8, 0, 0, 0, 0, 0, 0)); // Reserved for documentation and examples.
89 
90     let net = Network::default();
91 
92     test_udp_bind_ephemeral_port(&net, IpAddress::IPV4_LOOPBACK);
93     test_udp_bind_ephemeral_port(&net, IpAddress::IPV4_UNSPECIFIED);
94 
95     test_udp_bind_specific_port(&net, IpAddress::IPV4_LOOPBACK);
96     test_udp_bind_specific_port(&net, IpAddress::IPV4_UNSPECIFIED);
97 
98     test_udp_bind_addrinuse(&net, IpAddress::IPV4_LOOPBACK);
99     test_udp_bind_addrinuse(&net, IpAddress::IPV4_UNSPECIFIED);
100 
101     test_udp_bind_addrnotavail(&net, RESERVED_IPV4_ADDRESS);
102 
103     test_udp_bind_wrong_family(&net, IpAddressFamily::Ipv4);
104     test_udp_bind_wrong_family(&net, IpAddressFamily::Ipv6);
105 
106     if supports_ipv6() {
107         test_udp_bind_ephemeral_port(&net, IpAddress::IPV6_LOOPBACK);
108         test_udp_bind_ephemeral_port(&net, IpAddress::IPV6_UNSPECIFIED);
109 
110         test_udp_bind_specific_port(&net, IpAddress::IPV6_LOOPBACK);
111         test_udp_bind_specific_port(&net, IpAddress::IPV6_UNSPECIFIED);
112 
113         test_udp_bind_addrinuse(&net, IpAddress::IPV6_LOOPBACK);
114         test_udp_bind_addrinuse(&net, IpAddress::IPV6_UNSPECIFIED);
115 
116         test_udp_bind_addrnotavail(&net, RESERVED_IPV6_ADDRESS);
117 
118         test_udp_bind_dual_stack(&net);
119     }
120 }
121