socket_client: resolve the UDP target, do not shake hands with it

`test_target` opened a TCP connection to the rendezvous server to learn
which address the kernel would use, then dropped it: a full handshake, one
round trip, on every connection the controller starts, before the one
`_start_inner` opens to the same host a moment later - and a second's wait
first wherever an IPv6 route led to nothing, before it fell back to the
resolver anyway.

The handshake stood in for a choice that had been made by hand: before
2022's NAT64 fix the code preferred an A record outright, which a v6-only
host cannot reach, and asking the server itself was the way to let the OS
decide. The resolver decides the same way. getaddrinfo orders what it
returns as RFC 6724 says, looking up a route and a source address for each
candidate as it goes, so the synthesized IPv6 comes first on a v6-only host
and IPv6 on a dual-stack one, the order the handshake would have tried
them in. Its first answer is the address, and the fallback branch is all
that is left.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019aokqJuhjvB3kijXtAg5Ns
This commit is contained in:
rustdesk
2026-09-18 15:40:50 +08:00
co-authored by Claude Fable 5.1
parent 6e0d667b69
commit f688d4116c
+3 -5
View File
@@ -203,12 +203,10 @@ pub fn ipv4_to_ipv6(addr: String, ipv4: bool) -> String {
addr
}
/// The address of `target` this machine sends to: the resolver's first, ordered as RFC 6724 has
/// it with a route looked up for each, so NAT64's synthesized IPv6 comes first on a v6-only host.
/// A handshake used to ask the server the same, a round trip a connection and 1 s past a dead v6.
async fn test_target(target: &str) -> ResultType<SocketAddr> {
if let Ok(Ok(s)) = super::timeout(1000, tokio::net::TcpStream::connect(target)).await {
if let Ok(addr) = s.peer_addr() {
return Ok(addr);
}
}
tokio::net::lookup_host(target)
.await?
.next()