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