mirror of
https://github.com/tailscale/tailscale.git
synced 2026-09-22 11:35:12 -04:00
The forwarder's upstream UDP socket is unconnected, so any host that could send a datagram to its ephemeral port reached the single ReadFromUDPAddrPort call that decided the query: a datagram with the right 16-bit transaction ID was returned to the client as the resolver's answer no matter what address it came from, and a datagram with the wrong ID failed the query outright, handing it to the TCP fallback (or to nothing at all, with TCP retries disabled). sendUDP now keeps reading until a datagram arrives that could be a reply to the query it sent: one from the resolver's address and port carrying the transaction ID of the request. Other datagrams are dropped and counted, so neither a spoofed reply nor a single stray datagram can answer or end the query. This is the same source check the kernel applies to a connected socket; the socket stays unconnected because its ListenPacket path is what binds the query to a link (IP_BOUND_IF on macOS, ForwardLinkSelector elsewhere). The read loop still ends when the query context expires and closeOnCtxDone closes the conn, as before. On Windows an oversized datagram is reported as a truncation error without a source address, so there the transaction ID remains the only check, as before. Thanks to Ben Carman for the report! Updates tailscale/corp#48187 Reported-by: Ben Carman Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com> Change-Id: Ibabbec5c82739b66caa6fb943faa376c39adbf64