Skip to content

tailscale: fix preferred MagicDNS domain matching - #4400

Open
NagaseMinato wants to merge 220 commits into
SagerNet:testingfrom
NagaseMinato:fix/tailscale-preferred-domain
Open

tailscale: fix preferred MagicDNS domain matching#4400
NagaseMinato wants to merge 220 commits into
SagerNet:testingfrom
NagaseMinato:fix/tailscale-preferred-domain

Conversation

@NagaseMinato

@NagaseMinato NagaseMinato commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Background

A Tailscale endpoint may fail to match a MagicDNS FQDN in a route rule using preferred_by.

For example:

{
  "endpoints": [
    {
      "type": "tailscale",
      "tag": "ts-ep"
    }
  ],
  "inbounds": [
    {
      "type": "mixed",
      "tag": "mixed-in",
      "listen": "127.0.0.1",
      "listen_port": 8080
    }
  ],
  "route": {
    "rules": [
      {
        "preferred_by": "ts-ep",
        "action": "route",
        "outbound": "ts-ep"
      }
    ]
  }
}

When accessing a MagicDNS host through the HTTP proxy:

curl --proxy http://127.0.0.1:8080 https://example.tailnet.ts.net/

the mixed inbound (also Socks, HTTP, Shadowsocks and etc.) passes the destination domain directly to route matching. No DNS resolution has occurred at this point, so the rule relies on Endpoint.PreferredDomain.

The existing implementation only checks ExportMagicDNSHosts() when MagicDNSHostsUnrouted is true. However, this flag indicates whether MagicDNS host records are covered by DNS routes, rather than whether a domain belongs to MagicDNS. It is false when MagicDNS domain routing is enabled, causing the valid host above to miss the route rule.

The Tailscale DNS transport already handles this correctly. During DNS reconfiguration, it stores the result of ExportMagicDNSHosts(). Its PreferredDomain implementation passes that registry to lookupHosts without checking MagicDNSHostsUnrouted.

DNS rules using preferred_by therefore match the MagicDNS host, while route rules do not because they use the endpoint's separate PreferredDomain implementation.

nekohasekai and others added 26 commits August 8, 2026 11:30
Recent Windows 11 builds remove TCP estats, which made writeAndWaitAck
return without waiting for acknowledgment. sing now queries
SIO_TCP_INFO on the socket instead, and falls back to estats on
systems predating it (Windows 10 1703).
Batched darwin packet I/O now also covers connected sockets on iOS.
@nekohasekai
nekohasekai force-pushed the testing branch 4 times, most recently from 9b20d01 to 426c5fa Compare August 9, 2026 06:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants