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