mirror of
https://github.com/rustdesk/rustdesk.git
synced 2026-09-08 13:31:03 +03:00
fix: KCP/UDP resilience to ICMP resets; optional KCP congestion control
- treat ICMP-driven UDP socket errors (WSAECONNRESET 10054 on Windows, ECONNREFUSED on Linux) as packet loss in punch_udp and the KCP pump instead of tearing the session down; KCP retransmits through them and a truly dead link is still reaped by the pong/app-level timeouts - resolve STUN hostnames via tokio::net::lookup_host so DNS never blocks a runtime worker; fix the inverted non-IPv4 error message - add enable-kcp-congestion-control option (default on): switch the turbo profile to nc=0 so brief loss on constrained links no longer spirals into stalls; sender-side only, no wire negotiation - pin kcp-sys to the rustdesk-patches branch: upstream main lost the RustDesk patches on the EasyTier sync, and this branch also wires set_kcp_config_factory into connection setup, making the option effective Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
future::Future,
|
||||
net::{SocketAddr, ToSocketAddrs},
|
||||
net::SocketAddr,
|
||||
sync::{Arc, Mutex, RwLock},
|
||||
task::Poll,
|
||||
};
|
||||
@@ -2442,16 +2442,27 @@ pub fn is_udp_disabled() -> bool {
|
||||
Config::get_option(keys::OPTION_DISABLE_UDP) == "Y"
|
||||
}
|
||||
|
||||
pub const OPTION_ENABLE_KCP_CC: &str = "enable-kcp-congestion-control";
|
||||
|
||||
// Default ON ("enable-" option2bool semantics); set "N" to fall back to the pure
|
||||
// turbo profile (nc=1, no congestion window).
|
||||
#[inline]
|
||||
pub fn get_kcp_cc_enabled() -> bool {
|
||||
config::option2bool(
|
||||
OPTION_ENABLE_KCP_CC,
|
||||
&Config::get_option(OPTION_ENABLE_KCP_CC),
|
||||
)
|
||||
}
|
||||
|
||||
// this crate https://github.com/yoshd/stun-client supports nat type
|
||||
async fn stun_ipv6_test(stun_server: &str) -> ResultType<(SocketAddr, String)> {
|
||||
use std::net::ToSocketAddrs;
|
||||
use stunclient::StunClient;
|
||||
let local_addr = SocketAddr::from(([0u16; 8], 0)); // [::]:0
|
||||
let socket = UdpSocket::bind(&local_addr).await?;
|
||||
let Some(stun_addr) = stun_server
|
||||
.to_socket_addrs()?
|
||||
.filter(|x| x.is_ipv6())
|
||||
.next()
|
||||
// Resolve via tokio so DNS never blocks the async runtime worker.
|
||||
let Some(stun_addr) = tokio::net::lookup_host(stun_server)
|
||||
.await?
|
||||
.find(|x| x.is_ipv6())
|
||||
else {
|
||||
bail!(
|
||||
"Failed to resolve STUN ipv6 server address: {}",
|
||||
@@ -2468,14 +2479,13 @@ async fn stun_ipv6_test(stun_server: &str) -> ResultType<(SocketAddr, String)> {
|
||||
}
|
||||
|
||||
async fn stun_ipv4_test(stun_server: &str) -> ResultType<(SocketAddr, String)> {
|
||||
use std::net::ToSocketAddrs;
|
||||
use stunclient::StunClient;
|
||||
let local_addr = SocketAddr::from(([0u8; 4], 0));
|
||||
let socket = UdpSocket::bind(&local_addr).await?;
|
||||
let Some(stun_addr) = stun_server
|
||||
.to_socket_addrs()?
|
||||
.filter(|x| x.is_ipv4())
|
||||
.next()
|
||||
// Resolve via tokio so DNS never blocks the async runtime worker.
|
||||
let Some(stun_addr) = tokio::net::lookup_host(stun_server)
|
||||
.await?
|
||||
.find(|x| x.is_ipv4())
|
||||
else {
|
||||
bail!(
|
||||
"Failed to resolve STUN ipv4 server address: {}",
|
||||
@@ -2487,7 +2497,7 @@ async fn stun_ipv4_test(stun_server: &str) -> ResultType<(SocketAddr, String)> {
|
||||
Ok(if addr.ip().is_ipv4() {
|
||||
(addr, stun_server.to_owned())
|
||||
} else {
|
||||
bail!("STUN server returned non-IPv6 address: {}", addr)
|
||||
bail!("STUN server returned non-IPv4 address: {}", addr)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2526,10 +2536,9 @@ pub async fn test_nat_ipv4() -> ResultType<(SocketAddr, String)> {
|
||||
async fn test_bind_ipv6() -> ResultType<SocketAddr> {
|
||||
let local_addr = SocketAddr::from(([0u16; 8], 0)); // [::]:0
|
||||
let socket = UdpSocket::bind(local_addr).await?;
|
||||
let addr = STUNS_V6[0]
|
||||
.to_socket_addrs()?
|
||||
.filter(|x| x.is_ipv6())
|
||||
.next()
|
||||
let addr = tokio::net::lookup_host(STUNS_V6[0])
|
||||
.await?
|
||||
.find(|x| x.is_ipv6())
|
||||
.ok_or_else(|| {
|
||||
anyhow!(
|
||||
"Failed to resolve STUN ipv6 server address: {}",
|
||||
@@ -2660,7 +2669,14 @@ pub async fn punch_udp(
|
||||
}
|
||||
}
|
||||
res = socket.recv(&mut data) => match res {
|
||||
Err(e) => bail!("UDP punch failed, {packets_sent} packets sent: {e}"),
|
||||
Err(e) => {
|
||||
// While the hole is still forming, ICMP unreachable from the peer's NAT
|
||||
// is expected and surfaces as ConnectionReset/Refused on a connected
|
||||
// socket (notably 10054 on Windows). Treat it as loss and keep punching;
|
||||
// MAX_TIME above still bounds the whole attempt.
|
||||
log::debug!("UDP punch recv error (treated as loss): {e}");
|
||||
hbb_common::sleep(0.01).await;
|
||||
}
|
||||
Ok(n) => {
|
||||
// log::debug!("UDP punch succeeded after sending {} packets after {:?}", packets_sent, tm.elapsed());
|
||||
if listen {
|
||||
|
||||
Reference in New Issue
Block a user