|
1 | | -use test_programs::p3::wasi::sockets::types::{ |
2 | | - ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, UdpSocket, |
| 1 | +use test_programs::{ |
| 2 | + p3::wasi::sockets::types::{ |
| 3 | + ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Ipv4Address, Ipv6Address, UdpSocket, |
| 4 | + }, |
| 5 | + sockets::supports_ipv6, |
3 | 6 | }; |
4 | 7 |
|
5 | 8 | struct Component; |
6 | 9 |
|
7 | 10 | test_programs::p3::export!(Component); |
8 | 11 |
|
9 | | -const SOME_PORT: u16 = 47; // If the tests pass, this will never actually be connected to. |
| 12 | +// If the tests work as expected, these will never actually be connected to: |
| 13 | +const SOME_PORT: u16 = 47; |
| 14 | +const SOME_PUBLIC_IPV4: Ipv4Address = (123, 234, 12, 34); |
| 15 | +const SOME_PUBLIC_IPV6: Ipv6Address = (123, 234, 0, 0, 0, 0, 0, 34); |
10 | 16 |
|
11 | 17 | fn test_udp_connect_disconnect_reconnect(family: IpAddressFamily) { |
12 | 18 | let remote1 = IpSocketAddress::new(IpAddress::new_loopback(family), 4321); |
@@ -48,6 +54,38 @@ fn test_udp_connect_unspec(family: IpAddressFamily) { |
48 | 54 | )); |
49 | 55 | } |
50 | 56 |
|
| 57 | +/// If not explicitly bound, connecting a UDP socket should update the local |
| 58 | +/// address to reflect the best network path. |
| 59 | +fn test_udp_connect_local_address_change(family: IpAddressFamily) { |
| 60 | + fn connect(sock: &UdpSocket, ip: IpAddress, port: u16) -> IpSocketAddress { |
| 61 | + let remote = IpSocketAddress::new(ip, port); |
| 62 | + sock.connect(remote).unwrap(); |
| 63 | + let local = sock.get_local_address().unwrap(); |
| 64 | + println!("connect({remote:?}) changed local address to: {local:?}",); |
| 65 | + local |
| 66 | + } |
| 67 | + |
| 68 | + if !has_public_interface(family) { |
| 69 | + println!("No public interface detected, skipping test"); |
| 70 | + return; |
| 71 | + } |
| 72 | + |
| 73 | + let loopback_ip = IpAddress::new_loopback(family); |
| 74 | + let public_ip = some_public_ip(family); |
| 75 | + |
| 76 | + let client = UdpSocket::create(family).unwrap(); |
| 77 | + |
| 78 | + let loopback_if1 = connect(&client, loopback_ip, 4321); |
| 79 | + let loopback_if2 = connect(&client, loopback_ip, 4322); |
| 80 | + let public_if = connect(&client, public_ip, 4323); |
| 81 | + |
| 82 | + // Note: these assertions are based on observed behavior on Linux, MacOS and |
| 83 | + // Windows, but there is nothing in their official documentation to |
| 84 | + // corroborate this. |
| 85 | + assert_eq!(loopback_if1, loopback_if2); |
| 86 | + assert_ne!(loopback_if1, public_if); |
| 87 | +} |
| 88 | + |
51 | 89 | /// 0 is not a valid remote port. |
52 | 90 | fn test_udp_connect_port_0(family: IpAddressFamily) { |
53 | 91 | let addr = IpSocketAddress::new(IpAddress::new_loopback(family), 0); |
@@ -131,26 +169,38 @@ fn test_udp_connect_dual_stack() { |
131 | 169 | impl test_programs::p3::exports::wasi::cli::run::Guest for Component { |
132 | 170 | async fn run() -> Result<(), ()> { |
133 | 171 | test_udp_connect_disconnect_reconnect(IpAddressFamily::Ipv4); |
134 | | - test_udp_connect_disconnect_reconnect(IpAddressFamily::Ipv6); |
135 | | - |
136 | 172 | test_udp_connect_unspec(IpAddressFamily::Ipv4); |
137 | | - test_udp_connect_unspec(IpAddressFamily::Ipv6); |
138 | | - |
| 173 | + test_udp_connect_local_address_change(IpAddressFamily::Ipv4); |
139 | 174 | test_udp_connect_port_0(IpAddressFamily::Ipv4); |
140 | | - test_udp_connect_port_0(IpAddressFamily::Ipv6); |
141 | | - |
142 | 175 | test_udp_connect_wrong_family(IpAddressFamily::Ipv4); |
143 | | - test_udp_connect_wrong_family(IpAddressFamily::Ipv6); |
144 | | - |
145 | 176 | test_udp_connect_without_bind(IpAddressFamily::Ipv4); |
146 | | - test_udp_connect_without_bind(IpAddressFamily::Ipv6); |
147 | | - |
148 | 177 | test_udp_connect_with_bind(IpAddressFamily::Ipv4); |
149 | | - test_udp_connect_with_bind(IpAddressFamily::Ipv6); |
150 | 178 |
|
151 | | - test_udp_connect_dual_stack(); |
| 179 | + if supports_ipv6() { |
| 180 | + test_udp_connect_disconnect_reconnect(IpAddressFamily::Ipv6); |
| 181 | + test_udp_connect_unspec(IpAddressFamily::Ipv6); |
| 182 | + test_udp_connect_local_address_change(IpAddressFamily::Ipv6); |
| 183 | + test_udp_connect_port_0(IpAddressFamily::Ipv6); |
| 184 | + test_udp_connect_wrong_family(IpAddressFamily::Ipv6); |
| 185 | + test_udp_connect_without_bind(IpAddressFamily::Ipv6); |
| 186 | + test_udp_connect_with_bind(IpAddressFamily::Ipv6); |
| 187 | + test_udp_connect_dual_stack(); |
| 188 | + } |
152 | 189 | Ok(()) |
153 | 190 | } |
154 | 191 | } |
155 | 192 |
|
| 193 | +fn some_public_ip(family: IpAddressFamily) -> IpAddress { |
| 194 | + match family { |
| 195 | + IpAddressFamily::Ipv4 => IpAddress::Ipv4(SOME_PUBLIC_IPV4), |
| 196 | + IpAddressFamily::Ipv6 => IpAddress::Ipv6(SOME_PUBLIC_IPV6), |
| 197 | + } |
| 198 | +} |
| 199 | + |
| 200 | +fn has_public_interface(family: IpAddressFamily) -> bool { |
| 201 | + let sock = UdpSocket::create(family).unwrap(); |
| 202 | + sock.connect(IpSocketAddress::new(some_public_ip(family), SOME_PORT)) |
| 203 | + .is_ok() |
| 204 | +} |
| 205 | + |
156 | 206 | fn main() {} |
0 commit comments