Skip to content

Commit 52f8f19

Browse files
authored
Unconditionally set SO_REUSEADDR (#12597)
1 parent df57990 commit 52f8f19

2 files changed

Lines changed: 45 additions & 31 deletions

File tree

crates/test-programs/src/bin/p3_sockets_tcp_bind.rs

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -56,31 +56,43 @@ async fn test_tcp_bind_reuseaddr(ip: IpAddress) {
5656
let bind_addr = {
5757
let listener1 = TcpSocket::create(ip.family()).unwrap();
5858

59-
let bind_addr = attempt_random_port(ip, |bind_addr| listener1.bind(bind_addr)).unwrap();
60-
61-
let mut accept = listener1.listen().unwrap();
62-
63-
let connect_addr =
64-
IpSocketAddress::new(IpAddress::new_loopback(ip.family()), bind_addr.port());
65-
join!(
66-
async {
67-
client.connect(connect_addr).await.unwrap();
68-
},
69-
async {
70-
let sock = accept.next().await.unwrap();
71-
let (mut data_tx, data_rx) = wit_stream::new();
72-
join!(
73-
async {
74-
sock.send(data_rx).await.unwrap();
75-
},
76-
async {
77-
let remaining = data_tx.write_all(vec![0; 10]).await;
78-
assert!(remaining.is_empty());
79-
drop(data_tx);
80-
}
81-
);
82-
},
83-
);
59+
listener1
60+
.bind(IpSocketAddress::new(
61+
IpAddress::new_loopback(ip.family()),
62+
0,
63+
))
64+
.unwrap();
65+
66+
let bind_addr = listener1.get_local_address().unwrap();
67+
68+
// The listener socket must have at least one connection for the TIME_WAIT
69+
// mechanism to kick in. So we'll create & accept a dummy connection
70+
// before closing the listener:
71+
{
72+
let mut accept = listener1.listen().unwrap();
73+
74+
let connect_addr =
75+
IpSocketAddress::new(IpAddress::new_loopback(ip.family()), bind_addr.port());
76+
join!(
77+
async {
78+
client.connect(connect_addr).await.unwrap();
79+
},
80+
async {
81+
let sock = accept.next().await.unwrap();
82+
let (mut data_tx, data_rx) = wit_stream::new();
83+
join!(
84+
async {
85+
sock.send(data_rx).await.unwrap();
86+
},
87+
async {
88+
let remaining = data_tx.write_all(vec![0; 10]).await;
89+
assert!(remaining.is_empty());
90+
drop(data_tx);
91+
}
92+
);
93+
},
94+
);
95+
}
8496

8597
bind_addr
8698
};

crates/wasi/src/sockets/util.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -337,13 +337,15 @@ pub fn tcp_bind(
337337
socket: &tokio::net::TcpSocket,
338338
local_address: SocketAddr,
339339
) -> Result<(), ErrorCode> {
340-
// Automatically bypass the TIME_WAIT state when binding to a specific port
341-
// Unconditionally (re)set SO_REUSEADDR, even when the value is false.
342-
// This ensures we're not accidentally affected by any socket option
343-
// state left behind by a previous failed call to this method.
340+
// From the WASI spec:
341+
// > The bind operation shouldn't be affected by the TIME_WAIT state of a
342+
// > recently closed socket on the same local address. In practice this
343+
// > means that the SO_REUSEADDR socket option should be set implicitly on
344+
// > all platforms, except on Windows where this is the default behavior
345+
// > and SO_REUSEADDR performs something different.
344346
#[cfg(not(windows))]
345-
if let Err(err) = sockopt::set_socket_reuseaddr(&socket, local_address.port() > 0) {
346-
return Err(err.into());
347+
{
348+
_ = sockopt::set_socket_reuseaddr(&socket, true);
347349
}
348350

349351
// Perform the OS bind call.

0 commit comments

Comments
 (0)