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