udp: make the punch prove itself, and keep the listener answering

punch_udp sent a zero-length datagram and called the hole open on whatever
arrived next. The rendezvous NAT test's own leftover replies satisfy that
immediately - connect() does not flush the receive queue - so the retry loop
never ran and success meant nothing. The dead socket then cost KCP its full
timeout to rediscover, which is how a failed punch came to take 18 seconds.

Probes now carry a magic and a 64-bit transaction id, and both ends answer
each other's probes, so returning is a fact: a reply echoing our own id is the
one thing that proves the pair carries traffic both ways. With failure now
distinguishable from 'not yet', the window drops from 20s to 3s.

Two asymmetries fall out of that:

Only the connector stops on its own acknowledgement, because only it has
something to send next. An acknowledgement proves our probe came back, not
that the peer's probe was answered - and after punch_udp returns nothing
answers probes any more, since KCP's io loop drops anything shorter than its
header. A listener that stopped there would go mute while a peer whose own
probe or answer was lost - the normal state of a hole still opening - kept
probing an endpoint that works, until it timed out.

So the listener stops on the peer's first real packet instead, and hands that
packet to KcpStream::accept as its init_packet: its arrival proves the pair as
well as an acknowledgement would, and KCP never retransmits its SYN.
This commit is contained in:
rustdesk
2026-08-26 19:02:05 +08:00
parent e50d620889
commit 81703e2a82
2 changed files with 80 additions and 31 deletions

View File

@@ -1192,11 +1192,11 @@ async fn udp_nat_listen(
let socket_cloned = socket.clone();
let func = async {
socket.connect(peer_addr).await?;
let res = crate::punch_udp(socket.clone(), true).await?;
let init_packet = crate::punch_udp(socket.clone(), true).await?;
let stream = crate::kcp_stream::KcpStream::accept(
socket,
Duration::from_millis(CONNECT_TIMEOUT as _),
res,
init_packet,
)
.await?;
crate::server::create_tcp_connection(server, stream.1, peer_addr_v4, true, meta).await?;