Keep server alive on transient UDP send errors
This commit is contained in:
+91
-16
@@ -980,7 +980,7 @@ async fn handle_native_client_hello(
|
|||||||
};
|
};
|
||||||
let body = protocol::to_body(&NativeServerHelloBody { hello })?;
|
let body = protocol::to_body(&NativeServerHelloBody { hello })?;
|
||||||
let out = protocol::encode_plain(PacketKind::NativeServerHello, pending_id, 1, 0, &body)?;
|
let out = protocol::encode_plain(PacketKind::NativeServerHello, pending_id, 1, 0, &body)?;
|
||||||
socket.send_to(&out, peer).await?;
|
let _ = send_udp(socket, &out, peer).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1058,7 +1058,7 @@ async fn handle_native_user_auth(
|
|||||||
SERVER_TO_CLIENT,
|
SERVER_TO_CLIENT,
|
||||||
&body,
|
&body,
|
||||||
)?;
|
)?;
|
||||||
socket.send_to(&out, peer).await?;
|
let _ = send_udp(socket, &out, peer).await?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1251,7 +1251,7 @@ async fn handle_native_user_auth(
|
|||||||
SERVER_TO_CLIENT,
|
SERVER_TO_CLIENT,
|
||||||
&body,
|
&body,
|
||||||
)?;
|
)?;
|
||||||
socket.send_to(&out, peer).await?;
|
let _ = send_udp(socket, &out, peer).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1359,7 +1359,7 @@ async fn handle_bootstrap_attach(
|
|||||||
SERVER_TO_CLIENT,
|
SERVER_TO_CLIENT,
|
||||||
&body,
|
&body,
|
||||||
)?;
|
)?;
|
||||||
socket.send_to(&out, peer).await?;
|
let _ = send_udp(socket, &out, peer).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1494,7 +1494,7 @@ async fn handle_ticket_attach(
|
|||||||
};
|
};
|
||||||
let body = protocol::to_body(&envelope)?;
|
let body = protocol::to_body(&envelope)?;
|
||||||
let out = protocol::encode_plain(PacketKind::AttachOk, client_id, 1, 0, &body)?;
|
let out = protocol::encode_plain(PacketKind::AttachOk, client_id, 1, 0, &body)?;
|
||||||
socket.send_to(&out, peer).await?;
|
let _ = send_udp(socket, &out, peer).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1512,7 +1512,7 @@ async fn send_reject_to_client(
|
|||||||
reason: reason.to_string(),
|
reason: reason.to_string(),
|
||||||
})?;
|
})?;
|
||||||
let out = protocol::encode_plain(PacketKind::AttachReject, client_id, 0, 0, &body)?;
|
let out = protocol::encode_plain(PacketKind::AttachReject, client_id, 0, 0, &body)?;
|
||||||
socket.send_to(&out, peer).await?;
|
let _ = send_udp(socket, &out, peer).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1578,7 +1578,7 @@ async fn handle_resume(
|
|||||||
SERVER_TO_CLIENT,
|
SERVER_TO_CLIENT,
|
||||||
&body,
|
&body,
|
||||||
)?;
|
)?;
|
||||||
socket.send_to(&out, peer).await?;
|
let _ = send_udp(socket, &out, peer).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1690,7 +1690,7 @@ async fn handle_ping(
|
|||||||
SERVER_TO_CLIENT,
|
SERVER_TO_CLIENT,
|
||||||
b"",
|
b"",
|
||||||
)?;
|
)?;
|
||||||
socket.send_to(&out, peer).await?;
|
let _ = send_udp(socket, &out, peer).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1705,6 +1705,60 @@ fn remove_client(state: &Arc<Mutex<ServerState>>, client_id: [u8; 16]) {
|
|||||||
locked.remove_client_everywhere(&client_id);
|
locked.remove_client_everywhere(&client_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn send_udp(socket: &UdpSocket, packet: &[u8], peer: SocketAddr) -> Result<bool> {
|
||||||
|
match socket.send_to(packet, peer).await {
|
||||||
|
Ok(_) => Ok(true),
|
||||||
|
Err(err) if is_transient_udp_send_error(&err) => Ok(false),
|
||||||
|
Err(err) => Err(err).with_context(|| format!("send UDP packet to {peer}")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_transient_udp_send_error(err: &std::io::Error) -> bool {
|
||||||
|
matches!(
|
||||||
|
err.kind(),
|
||||||
|
std::io::ErrorKind::Interrupted
|
||||||
|
| std::io::ErrorKind::TimedOut
|
||||||
|
| std::io::ErrorKind::WouldBlock
|
||||||
|
) || err.raw_os_error().is_some_and(is_transient_udp_os_error)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn is_transient_udp_os_error(code: i32) -> bool {
|
||||||
|
matches!(
|
||||||
|
code,
|
||||||
|
libc::EADDRNOTAVAIL
|
||||||
|
| libc::ECONNREFUSED
|
||||||
|
| libc::ECONNRESET
|
||||||
|
| libc::EHOSTDOWN
|
||||||
|
| libc::EHOSTUNREACH
|
||||||
|
| libc::ENETDOWN
|
||||||
|
| libc::ENETRESET
|
||||||
|
| libc::ENETUNREACH
|
||||||
|
| libc::ETIMEDOUT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn is_transient_udp_os_error(code: i32) -> bool {
|
||||||
|
matches!(
|
||||||
|
code,
|
||||||
|
10049 // WSAEADDRNOTAVAIL
|
||||||
|
| 10050 // WSAENETDOWN
|
||||||
|
| 10051 // WSAENETUNREACH
|
||||||
|
| 10052 // WSAENETRESET
|
||||||
|
| 10054 // WSAECONNRESET
|
||||||
|
| 10060 // WSAETIMEDOUT
|
||||||
|
| 10061 // WSAECONNREFUSED
|
||||||
|
| 10064 // WSAEHOSTDOWN
|
||||||
|
| 10065 // WSAEHOSTUNREACH
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(unix, windows)))]
|
||||||
|
fn is_transient_udp_os_error(_code: i32) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
async fn handle_ack(
|
async fn handle_ack(
|
||||||
state: &Arc<Mutex<ServerState>>,
|
state: &Arc<Mutex<ServerState>>,
|
||||||
peer: SocketAddr,
|
peer: SocketAddr,
|
||||||
@@ -3020,7 +3074,7 @@ async fn send_stream_open_to_client(
|
|||||||
found
|
found
|
||||||
};
|
};
|
||||||
if let Some((endpoint, packet)) = send {
|
if let Some((endpoint, packet)) = send {
|
||||||
socket.send_to(&packet, endpoint).await?;
|
let _ = send_udp(socket, &packet, endpoint).await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -3114,7 +3168,7 @@ async fn queue_or_send_stream_data_to_client(
|
|||||||
send
|
send
|
||||||
};
|
};
|
||||||
if let Some((endpoint, packet)) = send {
|
if let Some((endpoint, packet)) = send {
|
||||||
socket.send_to(&packet, endpoint).await?;
|
let _ = send_udp(socket, &packet, endpoint).await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -3189,7 +3243,7 @@ async fn flush_stream_pending_data_to_client(
|
|||||||
let Some((endpoint, packet)) = send else {
|
let Some((endpoint, packet)) = send else {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
};
|
};
|
||||||
socket.send_to(&packet, endpoint).await?;
|
let _ = send_udp(socket, &packet, endpoint).await?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3333,7 +3387,7 @@ async fn send_stream_packet_to_client(
|
|||||||
found
|
found
|
||||||
};
|
};
|
||||||
if let Some((endpoint, packet)) = send {
|
if let Some((endpoint, packet)) = send {
|
||||||
socket.send_to(&packet, endpoint).await?;
|
let _ = send_udp(socket, &packet, endpoint).await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -3435,7 +3489,7 @@ async fn broadcast_output(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (endpoint, packet) in sends {
|
for (endpoint, packet) in sends {
|
||||||
socket.send_to(&packet, endpoint).await?;
|
let _ = send_udp(socket, &packet, endpoint).await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -3488,7 +3542,7 @@ async fn broadcast_session_exit(
|
|||||||
persist::remove_runtime_dir(&sessions_dir, &session_name);
|
persist::remove_runtime_dir(&sessions_dir, &session_name);
|
||||||
}
|
}
|
||||||
for (endpoint, packet) in sends {
|
for (endpoint, packet) in sends {
|
||||||
socket.send_to(&packet, endpoint).await?;
|
let _ = send_udp(socket, &packet, endpoint).await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -3595,7 +3649,7 @@ async fn retransmit_pending(
|
|||||||
sends
|
sends
|
||||||
};
|
};
|
||||||
for (endpoint, packet) in sends {
|
for (endpoint, packet) in sends {
|
||||||
socket.send_to(&packet, endpoint).await?;
|
let _ = send_udp(socket, &packet, endpoint).await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -3691,7 +3745,7 @@ async fn maybe_rekey_clients(
|
|||||||
sends
|
sends
|
||||||
};
|
};
|
||||||
for (endpoint, packet) in sends {
|
for (endpoint, packet) in sends {
|
||||||
socket.send_to(&packet, endpoint).await?;
|
let _ = send_udp(socket, &packet, endpoint).await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -4536,4 +4590,25 @@ mod tests {
|
|||||||
};
|
};
|
||||||
remote_bind_allowed(&config, "0.0.0.0").unwrap();
|
remote_bind_allowed(&config, "0.0.0.0").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
#[test]
|
||||||
|
fn server_udp_send_classifier_treats_network_roaming_errors_as_transient() {
|
||||||
|
for code in [
|
||||||
|
libc::EADDRNOTAVAIL,
|
||||||
|
libc::ECONNREFUSED,
|
||||||
|
libc::ECONNRESET,
|
||||||
|
libc::EHOSTDOWN,
|
||||||
|
libc::EHOSTUNREACH,
|
||||||
|
libc::ENETDOWN,
|
||||||
|
libc::ENETRESET,
|
||||||
|
libc::ENETUNREACH,
|
||||||
|
libc::ETIMEDOUT,
|
||||||
|
] {
|
||||||
|
assert!(
|
||||||
|
is_transient_udp_send_error(&std::io::Error::from_raw_os_error(code)),
|
||||||
|
"expected errno {code} to be transient"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user