From b4743f3b12360a8845396154cad413bc3e73a413 Mon Sep 17 00:00:00 2001 From: Dave Bakker Date: Thu, 14 Dec 2023 17:05:26 +0100 Subject: [PATCH 1/6] Restore SO_REUSEADDR. This inadvertently removed in 8ca80569452908d678066fb34a11d1ceda78e0c7 when switching from `bind_existing_tcp_listener` to `rustix::net::bind` --- .../src/bin/preview2_tcp_bind.rs | 40 +++++++++++++++++++ crates/wasi-http/wit/deps/sockets/tcp.wit | 5 +++ crates/wasi/src/preview2/host/network.rs | 27 +++++++++++++ crates/wasi/src/preview2/host/tcp.rs | 9 +++++ crates/wasi/wit/deps/sockets/tcp.wit | 5 +++ 5 files changed, 86 insertions(+) diff --git a/crates/test-programs/src/bin/preview2_tcp_bind.rs b/crates/test-programs/src/bin/preview2_tcp_bind.rs index faea346a155f..a966e18857dd 100644 --- a/crates/test-programs/src/bin/preview2_tcp_bind.rs +++ b/crates/test-programs/src/bin/preview2_tcp_bind.rs @@ -54,6 +54,43 @@ fn test_tcp_bind_addrinuse(net: &Network, ip: IpAddress) { ); } +// The WASI runtime should set SO_REUSEADDR for us +fn test_tcp_bind_reuseaddr(net: &Network, ip: IpAddress) { + const PORT: u16 = 54321; + + let bind_addr = IpSocketAddress::new(IpAddress::new_unspecified(ip.family()), PORT); + let connect_addr = IpSocketAddress::new(IpAddress::new_loopback(ip.family()), PORT); + + let client = TcpSocket::new(ip.family()).unwrap(); + + { + let listener1 = TcpSocket::new(ip.family()).unwrap(); + listener1.blocking_bind(net, bind_addr).unwrap(); + listener1.blocking_listen().unwrap(); + + client.blocking_connect(net, connect_addr).unwrap(); + + let (accepted_connection, accepted_input, accepted_output) = + listener1.blocking_accept().unwrap(); + accepted_output.blocking_write_zeroes_and_flush(10).unwrap(); + drop(accepted_input); + drop(accepted_output); + drop(accepted_connection); + drop(listener1); + } + + { + let listener2 = TcpSocket::new(ip.family()).unwrap(); + + // If SO_REUSEADDR was configured correctly, the following lines shouldn't be + // affected by the TIME_WAIT state of the just closed `listener1` socket: + listener2.blocking_bind(net, bind_addr).unwrap(); + listener2.blocking_listen().unwrap(); + } + + drop(client); +} + // Try binding to an address that is not configured on the system. fn test_tcp_bind_addrnotavail(net: &Network, ip: IpAddress) { let bind_addr = IpSocketAddress::new(ip, 0); @@ -141,6 +178,9 @@ fn main() { test_tcp_bind_specific_port(&net, IpAddress::IPV4_UNSPECIFIED); test_tcp_bind_specific_port(&net, IpAddress::IPV6_UNSPECIFIED); + test_tcp_bind_reuseaddr(&net, IpAddress::IPV4_LOOPBACK); + test_tcp_bind_reuseaddr(&net, IpAddress::IPV6_LOOPBACK); + test_tcp_bind_addrinuse(&net, IpAddress::IPV4_LOOPBACK); test_tcp_bind_addrinuse(&net, IpAddress::IPV6_LOOPBACK); test_tcp_bind_addrinuse(&net, IpAddress::IPV4_UNSPECIFIED); diff --git a/crates/wasi-http/wit/deps/sockets/tcp.wit b/crates/wasi-http/wit/deps/sockets/tcp.wit index b01b65e6c4b5..a0dcfe26feea 100644 --- a/crates/wasi-http/wit/deps/sockets/tcp.wit +++ b/crates/wasi-http/wit/deps/sockets/tcp.wit @@ -42,6 +42,11 @@ interface tcp { /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) /// - `not-in-progress`: A `bind` operation is not in progress. /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// When binding to a non-zero port, this bind operation shouldn't be affected the TIME_WAIT + /// state of a recently closed socket on the same local address. Ie. the SO_REUSEADDR socket + /// option should be set implicitly on platforms that require it. /// /// # References /// - diff --git a/crates/wasi/src/preview2/host/network.rs b/crates/wasi/src/preview2/host/network.rs index 55dedb1b145d..bee6814d3f9b 100644 --- a/crates/wasi/src/preview2/host/network.rs +++ b/crates/wasi/src/preview2/host/network.rs @@ -415,6 +415,33 @@ pub(crate) mod util { } } + // Even though SO_REUSEADDR is a SOL_* level option, this function contain a + // compatibility fix specific to TCP. That's why it contains the `_tcp_` infix instead of `_socket_`. + pub fn set_tcp_reuseaddr(sockfd: Fd, value: bool) -> rustix::io::Result<()> { + // When a TCP socket is closed, the system may + // temporarily reserve that specific address+port pair in a so called + // TIME_WAIT state. During that period, any attempt to rebind to that pair + // will fail. Setting SO_REUSEADDR to true bypasses that behaviour. Unlike + // the name "SO_REUSEADDR" might suggest, it does not allow multiple + // active sockets to share the same local address. + + // On Windows that behavior is the default, so there is no need to manually + // configure such an option. But (!), Windows _does_ have an identically + // named socket option which allows users to "hijack" active sockets. + // This is definitely not what we want to do here. + + // Microsoft's own documentation[1] states that we should set SO_EXCLUSIVEADDRUSE + // instead (to the inverse value), however the github issue below[2] seems + // to indicate that that may no longer be correct. + // [1]: https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse + // [2]: https://github.com/python-trio/trio/issues/928 + + #[cfg(not(windows))] + sockopt::set_socket_reuseaddr(sockfd, value)?; + + Ok(()) + } + pub fn set_tcp_keepidle(sockfd: Fd, value: Duration) -> rustix::io::Result<()> { if value <= Duration::ZERO { // WIT: "If the provided value is 0, an `invalid-argument` error is returned." diff --git a/crates/wasi/src/preview2/host/tcp.rs b/crates/wasi/src/preview2/host/tcp.rs index b1d4ba31f28c..928bd552219a 100644 --- a/crates/wasi/src/preview2/host/tcp.rs +++ b/crates/wasi/src/preview2/host/tcp.rs @@ -49,6 +49,15 @@ impl crate::preview2::host::tcp::tcp::HostTcpSocket for T { network.check_socket_addr(&local_address, SocketAddrUse::TcpBind)?; let listener = &*socket.tcp_socket().as_socketlike_view::(); + // Automatically bypass the TIME_WAIT state when the user is trying + // to bind to a specific port: + let reuse_addr = local_address.port() > 0; + + // Unconditionally (re)set SO_REUSEADDR, even when the value is false. + // This ensures we're not accidentally affected by any socket option + // state left behind by a previous failed call to this method (start_bind). + util::set_tcp_reuseaddr(socket.tcp_socket(), reuse_addr)?; + // Perform the OS bind call. util::tcp_bind(listener, &local_address).map_err(|error| { match Errno::from_io_error(&error) { diff --git a/crates/wasi/wit/deps/sockets/tcp.wit b/crates/wasi/wit/deps/sockets/tcp.wit index b01b65e6c4b5..a0dcfe26feea 100644 --- a/crates/wasi/wit/deps/sockets/tcp.wit +++ b/crates/wasi/wit/deps/sockets/tcp.wit @@ -42,6 +42,11 @@ interface tcp { /// - `address-not-bindable`: `local-address` is not an address that the `network` can bind to. (EADDRNOTAVAIL) /// - `not-in-progress`: A `bind` operation is not in progress. /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) + /// + /// # Implementors note + /// When binding to a non-zero port, this bind operation shouldn't be affected the TIME_WAIT + /// state of a recently closed socket on the same local address. Ie. the SO_REUSEADDR socket + /// option should be set implicitly on platforms that require it. /// /// # References /// - From e5c4526867791ba89be4e686b9fe485900f34bb5 Mon Sep 17 00:00:00 2001 From: Dave Bakker Date: Thu, 14 Dec 2023 17:47:09 +0100 Subject: [PATCH 2/6] Remove AddressInUse workarounds by generating a random port. --- .../src/bin/preview2_tcp_bind.rs | 29 ++++++++----------- .../src/bin/preview2_udp_bind.rs | 14 +++------ crates/test-programs/src/sockets.rs | 6 ++++ 3 files changed, 22 insertions(+), 27 deletions(-) diff --git a/crates/test-programs/src/bin/preview2_tcp_bind.rs b/crates/test-programs/src/bin/preview2_tcp_bind.rs index a966e18857dd..81e3f542db5d 100644 --- a/crates/test-programs/src/bin/preview2_tcp_bind.rs +++ b/crates/test-programs/src/bin/preview2_tcp_bind.rs @@ -1,3 +1,4 @@ +use test_programs::sockets::generate_random_port; use test_programs::wasi::sockets::network::{ ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Network, }; @@ -18,23 +19,17 @@ fn test_tcp_bind_ephemeral_port(net: &Network, ip: IpAddress) { /// Bind a socket on a specified port. fn test_tcp_bind_specific_port(net: &Network, ip: IpAddress) { - const PORT: u16 = 54321; + let port: u16 = generate_random_port(); - let bind_addr = IpSocketAddress::new(ip, PORT); + let bind_addr = IpSocketAddress::new(ip, port); let sock = TcpSocket::new(ip.family()).unwrap(); - match sock.blocking_bind(net, bind_addr) { - Ok(()) => { - let bound_addr = sock.local_address().unwrap(); - - assert_eq!(bind_addr.ip(), bound_addr.ip()); - assert_eq!(bind_addr.port(), bound_addr.port()); - } - // Concurrent invocations of this test can yield `AddressInUse` and that - // same error can show up on Windows as `AccessDenied`. - Err(ErrorCode::AddressInUse | ErrorCode::AccessDenied) => {} - Err(e) => panic!("error: {e}"), - } + sock.blocking_bind(net, bind_addr).unwrap(); + + let bound_addr = sock.local_address().unwrap(); + + assert_eq!(bind_addr.ip(), bound_addr.ip()); + assert_eq!(bind_addr.port(), bound_addr.port()); } /// Two sockets may not be actively bound to the same address at the same time. @@ -56,10 +51,10 @@ fn test_tcp_bind_addrinuse(net: &Network, ip: IpAddress) { // The WASI runtime should set SO_REUSEADDR for us fn test_tcp_bind_reuseaddr(net: &Network, ip: IpAddress) { - const PORT: u16 = 54321; + let port: u16 = generate_random_port(); - let bind_addr = IpSocketAddress::new(IpAddress::new_unspecified(ip.family()), PORT); - let connect_addr = IpSocketAddress::new(IpAddress::new_loopback(ip.family()), PORT); + let bind_addr = IpSocketAddress::new(IpAddress::new_unspecified(ip.family()), port); + let connect_addr = IpSocketAddress::new(IpAddress::new_loopback(ip.family()), port); let client = TcpSocket::new(ip.family()).unwrap(); diff --git a/crates/test-programs/src/bin/preview2_udp_bind.rs b/crates/test-programs/src/bin/preview2_udp_bind.rs index 4c5ccc91c48f..9478ac803dea 100644 --- a/crates/test-programs/src/bin/preview2_udp_bind.rs +++ b/crates/test-programs/src/bin/preview2_udp_bind.rs @@ -1,3 +1,4 @@ +use test_programs::sockets::generate_random_port; use test_programs::wasi::sockets::network::{ ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Network, }; @@ -18,19 +19,12 @@ fn test_udp_bind_ephemeral_port(net: &Network, ip: IpAddress) { /// Bind a socket on a specified port. fn test_udp_bind_specific_port(net: &Network, ip: IpAddress) { - const PORT: u16 = 54321; + let port: u16 = generate_random_port(); - let bind_addr = IpSocketAddress::new(ip, PORT); + let bind_addr = IpSocketAddress::new(ip, port); let sock = UdpSocket::new(ip.family()).unwrap(); - match sock.blocking_bind(net, bind_addr) { - Ok(()) => {} - - // Concurrent invocations of this test can yield `AddressInUse` and that - // same error can show up on Windows as `AccessDenied`. - Err(ErrorCode::AddressInUse | ErrorCode::AccessDenied) => return, - r => r.unwrap(), - } + sock.blocking_bind(net, bind_addr).unwrap(); let bound_addr = sock.local_address().unwrap(); diff --git a/crates/test-programs/src/sockets.rs b/crates/test-programs/src/sockets.rs index e4dc2d58b78f..2fd20acaa9df 100644 --- a/crates/test-programs/src/sockets.rs +++ b/crates/test-programs/src/sockets.rs @@ -1,6 +1,7 @@ use crate::wasi::clocks::monotonic_clock; use crate::wasi::io::poll::{self, Pollable}; use crate::wasi::io::streams::{InputStream, OutputStream, StreamError}; +use crate::wasi::random; use crate::wasi::sockets::instance_network; use crate::wasi::sockets::ip_name_lookup; use crate::wasi::sockets::network::{ @@ -357,3 +358,8 @@ impl PartialEq for IpSocketAddress { } } } + +pub fn generate_random_port() -> u16 { + let port = 15_000 + (random::random::get_random_u64() % 50_000); + port as u16 +} \ No newline at end of file From 03af478e0e9dc3783fdb0c3deb0883cde600f7ab Mon Sep 17 00:00:00 2001 From: Dave Bakker Date: Thu, 14 Dec 2023 18:31:24 +0100 Subject: [PATCH 3/6] fmt --- crates/test-programs/src/bin/preview2_tcp_bind.rs | 2 +- crates/test-programs/src/sockets.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/test-programs/src/bin/preview2_tcp_bind.rs b/crates/test-programs/src/bin/preview2_tcp_bind.rs index 81e3f542db5d..e5ff8f63422f 100644 --- a/crates/test-programs/src/bin/preview2_tcp_bind.rs +++ b/crates/test-programs/src/bin/preview2_tcp_bind.rs @@ -66,7 +66,7 @@ fn test_tcp_bind_reuseaddr(net: &Network, ip: IpAddress) { client.blocking_connect(net, connect_addr).unwrap(); let (accepted_connection, accepted_input, accepted_output) = - listener1.blocking_accept().unwrap(); + listener1.blocking_accept().unwrap(); accepted_output.blocking_write_zeroes_and_flush(10).unwrap(); drop(accepted_input); drop(accepted_output); diff --git a/crates/test-programs/src/sockets.rs b/crates/test-programs/src/sockets.rs index 2fd20acaa9df..1b157dff4851 100644 --- a/crates/test-programs/src/sockets.rs +++ b/crates/test-programs/src/sockets.rs @@ -362,4 +362,4 @@ impl PartialEq for IpSocketAddress { pub fn generate_random_port() -> u16 { let port = 15_000 + (random::random::get_random_u64() % 50_000); port as u16 -} \ No newline at end of file +} From 34a86844b7b1f775970ff1a99595cc9e51ca5012 Mon Sep 17 00:00:00 2001 From: Dave Bakker Date: Sun, 17 Dec 2023 10:10:20 +0100 Subject: [PATCH 4/6] Ignore unused_variables warning --- crates/wasi/src/preview2/host/network.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/wasi/src/preview2/host/network.rs b/crates/wasi/src/preview2/host/network.rs index bee6814d3f9b..b698eb5d57df 100644 --- a/crates/wasi/src/preview2/host/network.rs +++ b/crates/wasi/src/preview2/host/network.rs @@ -417,6 +417,7 @@ pub(crate) mod util { // Even though SO_REUSEADDR is a SOL_* level option, this function contain a // compatibility fix specific to TCP. That's why it contains the `_tcp_` infix instead of `_socket_`. + #[allow(unused_variables)] // Parameters are not used on Windows pub fn set_tcp_reuseaddr(sockfd: Fd, value: bool) -> rustix::io::Result<()> { // When a TCP socket is closed, the system may // temporarily reserve that specific address+port pair in a so called From 0e50254f235484f396759dc0d95bafd4038e9f80 Mon Sep 17 00:00:00 2001 From: Dave Bakker Date: Sun, 17 Dec 2023 12:23:37 +0100 Subject: [PATCH 5/6] Prevent spurious test failures by trying again a few times on EADDRINUSE --- .../src/bin/preview2_tcp_bind.rs | 28 ++++++++-------- .../src/bin/preview2_udp_bind.rs | 10 +++--- crates/test-programs/src/sockets.rs | 33 +++++++++++++++++-- 3 files changed, 49 insertions(+), 22 deletions(-) diff --git a/crates/test-programs/src/bin/preview2_tcp_bind.rs b/crates/test-programs/src/bin/preview2_tcp_bind.rs index e5ff8f63422f..8523dbe475df 100644 --- a/crates/test-programs/src/bin/preview2_tcp_bind.rs +++ b/crates/test-programs/src/bin/preview2_tcp_bind.rs @@ -1,4 +1,4 @@ -use test_programs::sockets::generate_random_port; +use test_programs::sockets::attempt_random_port; use test_programs::wasi::sockets::network::{ ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Network, }; @@ -19,12 +19,10 @@ fn test_tcp_bind_ephemeral_port(net: &Network, ip: IpAddress) { /// Bind a socket on a specified port. fn test_tcp_bind_specific_port(net: &Network, ip: IpAddress) { - let port: u16 = generate_random_port(); - - let bind_addr = IpSocketAddress::new(ip, port); - let sock = TcpSocket::new(ip.family()).unwrap(); - sock.blocking_bind(net, bind_addr).unwrap(); + + let bind_addr = + attempt_random_port(ip, |bind_addr| sock.blocking_bind(net, bind_addr)).unwrap(); let bound_addr = sock.local_address().unwrap(); @@ -51,18 +49,18 @@ fn test_tcp_bind_addrinuse(net: &Network, ip: IpAddress) { // The WASI runtime should set SO_REUSEADDR for us fn test_tcp_bind_reuseaddr(net: &Network, ip: IpAddress) { - let port: u16 = generate_random_port(); - - let bind_addr = IpSocketAddress::new(IpAddress::new_unspecified(ip.family()), port); - let connect_addr = IpSocketAddress::new(IpAddress::new_loopback(ip.family()), port); - let client = TcpSocket::new(ip.family()).unwrap(); - { + let bind_addr = { let listener1 = TcpSocket::new(ip.family()).unwrap(); - listener1.blocking_bind(net, bind_addr).unwrap(); + + let bind_addr = + attempt_random_port(ip, |bind_addr| listener1.blocking_bind(net, bind_addr)).unwrap(); + listener1.blocking_listen().unwrap(); + let connect_addr = + IpSocketAddress::new(IpAddress::new_loopback(ip.family()), bind_addr.port()); client.blocking_connect(net, connect_addr).unwrap(); let (accepted_connection, accepted_input, accepted_output) = @@ -72,7 +70,9 @@ fn test_tcp_bind_reuseaddr(net: &Network, ip: IpAddress) { drop(accepted_output); drop(accepted_connection); drop(listener1); - } + + bind_addr + }; { let listener2 = TcpSocket::new(ip.family()).unwrap(); diff --git a/crates/test-programs/src/bin/preview2_udp_bind.rs b/crates/test-programs/src/bin/preview2_udp_bind.rs index 9478ac803dea..f320a043026b 100644 --- a/crates/test-programs/src/bin/preview2_udp_bind.rs +++ b/crates/test-programs/src/bin/preview2_udp_bind.rs @@ -1,4 +1,4 @@ -use test_programs::sockets::generate_random_port; +use test_programs::sockets::attempt_random_port; use test_programs::wasi::sockets::network::{ ErrorCode, IpAddress, IpAddressFamily, IpSocketAddress, Network, }; @@ -19,12 +19,10 @@ fn test_udp_bind_ephemeral_port(net: &Network, ip: IpAddress) { /// Bind a socket on a specified port. fn test_udp_bind_specific_port(net: &Network, ip: IpAddress) { - let port: u16 = generate_random_port(); - - let bind_addr = IpSocketAddress::new(ip, port); - let sock = UdpSocket::new(ip.family()).unwrap(); - sock.blocking_bind(net, bind_addr).unwrap(); + + let bind_addr = + attempt_random_port(ip, |bind_addr| sock.blocking_bind(net, bind_addr)).unwrap(); let bound_addr = sock.local_address().unwrap(); diff --git a/crates/test-programs/src/sockets.rs b/crates/test-programs/src/sockets.rs index 1b157dff4851..c77ad8d7f819 100644 --- a/crates/test-programs/src/sockets.rs +++ b/crates/test-programs/src/sockets.rs @@ -359,7 +359,36 @@ impl PartialEq for IpSocketAddress { } } -pub fn generate_random_port() -> u16 { - let port = 15_000 + (random::random::get_random_u64() % 50_000); +fn generate_random_u16(range: Range) -> u16 { + let start = range.start as u64; + let end = range.end as u64; + let port = start + (random::random::get_random_u64() % (end - start)); port as u16 } + +/// Execute the inner function with a randomly generated port. +/// To prevent random failures, we make a few attempts before giving up. +pub fn attempt_random_port( + local_address: IpAddress, + mut f: F, +) -> Result +where + F: FnMut(IpSocketAddress) -> Result<(), ErrorCode>, +{ + const MAX_ATTEMPTS: u32 = 10; + let mut i = 0; + loop { + i += 1; + + let port: u16 = generate_random_u16(1024..u16::MAX); + let sock_addr = IpSocketAddress::new(local_address, port); + + match f(sock_addr) { + Ok(_) => return Ok(sock_addr), + Err(e) if i >= MAX_ATTEMPTS => return Err(e), + // Try again if the port is already taken. This can sometimes show up as `AccessDenied` on Windows. + Err(ErrorCode::AddressInUse | ErrorCode::AccessDenied) => {} + Err(e) => return Err(e), + } + } +} From 86d54182a85ef440749aac3a3e9212e1f6ed549d Mon Sep 17 00:00:00 2001 From: Dave Bakker Date: Sun, 17 Dec 2023 12:28:50 +0100 Subject: [PATCH 6/6] Fix grammar in .wit documentation --- crates/wasi-http/wit/deps/sockets/tcp.wit | 6 +++--- crates/wasi/wit/deps/sockets/tcp.wit | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/wasi-http/wit/deps/sockets/tcp.wit b/crates/wasi-http/wit/deps/sockets/tcp.wit index a0dcfe26feea..976b272c00a8 100644 --- a/crates/wasi-http/wit/deps/sockets/tcp.wit +++ b/crates/wasi-http/wit/deps/sockets/tcp.wit @@ -44,9 +44,9 @@ interface tcp { /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) /// /// # Implementors note - /// When binding to a non-zero port, this bind operation shouldn't be affected the TIME_WAIT - /// state of a recently closed socket on the same local address. Ie. the SO_REUSEADDR socket - /// option should be set implicitly on platforms that require it. + /// When binding to a non-zero port, this bind operation shouldn't be affected by the TIME_WAIT + /// state of a recently closed socket on the same local address (i.e. the SO_REUSEADDR socket + /// option should be set implicitly on platforms that require it). /// /// # References /// - diff --git a/crates/wasi/wit/deps/sockets/tcp.wit b/crates/wasi/wit/deps/sockets/tcp.wit index a0dcfe26feea..976b272c00a8 100644 --- a/crates/wasi/wit/deps/sockets/tcp.wit +++ b/crates/wasi/wit/deps/sockets/tcp.wit @@ -44,9 +44,9 @@ interface tcp { /// - `would-block`: Can't finish the operation, it is still in progress. (EWOULDBLOCK, EAGAIN) /// /// # Implementors note - /// When binding to a non-zero port, this bind operation shouldn't be affected the TIME_WAIT - /// state of a recently closed socket on the same local address. Ie. the SO_REUSEADDR socket - /// option should be set implicitly on platforms that require it. + /// When binding to a non-zero port, this bind operation shouldn't be affected by the TIME_WAIT + /// state of a recently closed socket on the same local address (i.e. the SO_REUSEADDR socket + /// option should be set implicitly on platforms that require it). /// /// # References /// -