Querying single-label names over tailscale dns by appending the tailnet search domain
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 38.2k
- Forks
- 4.6k
- Avg merge
- 19d 15h
- Merged PRs (30d)
- 1
Description
I want to query a single-label hostname (simple hostnames without any . in it, in the following example, I will use abc) over tailscale dns of singbox. But tailscale dns of sing-box will only resolve hostnames with the full tailnet-suffix.ts.net postfix. I.e. ping abc will fail, but ping abc.tailnet-suffix.ts.net will succeed.
I have been connecting to hosts only by the hostname over vanilla tailscale for a while. On both windows and linux (specifically, systemd-resolved described the mechinism to turn single label names to fully qualified domain names in systemd-resolved and VPNs), the default dns resolution mechinism will append the search domain while resolving single label names. Below is the output of resolvectl query --interface tailscale0 abc on a machine running tailscale daemon.
abc: 100.106.51.119 -- link: tailscale0
(abc.tailnet-suffix.ts.net)
-- Information acquired via protocol DNS in 3.7ms.
-- Data is authenticated: no; Data was acquired via local or encrypted transport: no
-- Data from: network
This also works for on windows machine. Resolve-DnsName -Name def -Server 100.100.100.100 outputs
Name Type TTL Section NameHost
---- ---- --- ------- --------
def CNAME 60480 Answer def.tailnet-suffix.ts.net.
0
Name : def.tailnet-suffix.ts.net.
QueryType : A
TTL : 604800
Section : Answer
IP4Address : 100.104.216.55
We can see from resolvectl status tailscale0 that tailscale set the search domain to tailnet-suffix.ts.net, thus ping abc just works.
Link 5 (tailscale0)
Current Scopes: DNS
Protocols: -DefaultRoute -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 100.100.100.100
DNS Servers: 100.100.100.100
DNS Domain: tailnet-suffix.ts.net ~0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa
~100.100.in-addr.arpa ~101.100.in-addr.arpa ~102.100.in-addr.arpa
~103.100.in-addr.arpa ~104.100.in-addr.arpa ~105.100.in-addr.arpa
~106.100.in-addr.arpa ~107.100.in-addr.arpa ~108.100.in-addr.arpa
~109.100.in-addr.arpa ~110.100.in-addr.arpa ~111.100.in-addr.arpa
~112.100.in-addr.arpa ~113.100.in-addr.arpa ~114.100.in-addr.arpa
~115.100.in-addr.arpa ~116.100.in-addr.arpa ~117.100.in-addr.arpa
~118.100.in-addr.arpa ~119.100.in-addr.arpa ~120.100.in-addr.arpa
~121.100.in-addr.arpa ~122.100.in-addr.arpa ~123.100.in-addr.arpa
~124.100.in-addr.arpa ~125.100.in-addr.arpa ~126.100.in-addr.arpa
~127.100.in-addr.arpa ~64.100.in-addr.arpa ~65.100.in-addr.arpa
~66.100.in-addr.arpa ~67.100.in-addr.arpa ~68.100.in-addr.arpa
~69.100.in-addr.arpa ~70.100.in-addr.arpa ~71.100.in-addr.arpa
~72.100.in-addr.arpa ~73.100.in-addr.arpa ~74.100.in-addr.arpa
~75.100.in-addr.arpa ~76.100.in-addr.arpa ~77.100.in-addr.arpa
~78.100.in-addr.arpa ~79.100.in-addr.arpa ~80.100.in-addr.arpa
~81.100.in-addr.arpa ~82.100.in-addr.arpa ~83.100.in-addr.arpa
~84.100.in-addr.arpa ~85.100.in-addr.arpa ~86.100.in-addr.arpa
~87.100.in-addr.arpa ~88.100.in-addr.arpa ~89.100.in-addr.arpa
~90.100.in-addr.arpa ~91.100.in-addr.arpa ~92.100.in-addr.arpa
~93.100.in-addr.arpa ~94.100.in-addr.arpa ~95.100.in-addr.arpa
~96.100.in-addr.arpa ~97.100.in-addr.arpa ~98.100.in-addr.arpa
~99.100.in-addr.arpa ~ts.net
Default Route: no
But this is not the case for sing-box tailscale dns. I have abc: 'abc' not found while running resolvectl query --interface singtun abc. If I append the domain suffix to the hostname resolvectl query --interface singtun abc.tailnet-suffix.ts.net, then everything works.
abc.tailnet-suffix.ts.net: 100.106.51.119 -- link: singtun
-- Information acquired via protocol DNS in 2.8ms.
-- Data is authenticated: no; Data was acquired via local or encrypted transport: no
-- Data from: network
I searched the code base and found out the relevant code to handle dns query over tailscale here https://github.com/SagerNet/sing-box/blob/00ec31142fc9e2441eba77a851989cfc91a7829b/protocol/tailscale/dns_transport.go#L241-L263 .
Why do you think appending the search domain obtained from tailscale control plane while resolving single label names in the code above? This would match the behavior of tailscale daemon's current behavior and make tailscale dns much more usable.
Here is my sing-box configuration.
{
"dns": {
"client_subnet": "223.5.5.0/24",
"final": "foreign",
"independent_cache": true,
"reverse_mapping": true,
"rules": [
{
"domain_regex": "(.*ts.net$|^[^.]+$)",
"ip_cidr": "100.64.0.0/10",
"server": "ts-dns"
},
{
"clash_mode": "direct",
"server": "domestic"
},
{
"clash_mode": "global",
"server": "fakeip"
},
{
"rule_set": "geosite-cn",
"server": "domestic"
},
{
"action": "reject",
"query_type": "HTTPS"
},
{
"query_type": [
"A",
"AAAA"
],
"rewrite_ttl": 1,
"server": "fakeip"
}
],
"servers": [
{
"accept_default_resolvers": false,
"endpoint": "ts-endpoint",
"tag": "ts-dns",
"type": "tailscale"
},
{
"server": "223.6.6.6",
"tag": "domestic",
"type": "https"
},
{
"detour": "auto",
"server": "8.8.8.8",
"tag": "foreign",
"type": "https"
},
{
"inet4_range": "198.18.0.0/15",
"inet6_range": "fc00::/18",
"tag": "fakeip",
"type": "fakeip"
}
],
"strategy": "ipv4_only"
},
"endpoints": [
{
"accept_routes": true,
"ephemeral": false,
"exit_node_allow_lan_access": true,
"hostname": "test",
"tag": "ts-endpoint",
"type": "tailscale"
}
],
"experimental": {
"clash_api": {
"external_controller": "127.0.0.1:9090",
"external_ui": "ui",
"external_ui_download_detour": "auto"
}
},
"inbounds": [
{
"listen": "::",
"listen_port": 7890,
"set_system_proxy": false,
"type": "mixed"
},
{
"address": [
"172.19.0.1/30",
"fdfe:dcba:9876::1/126"
],
"auto_redirect": true,
"auto_route": true,
"exclude_interface": [],
"interface_name": "singtun",
"mtu": 9000,
"platform": {
"http_proxy": {
"enabled": true,
"server": "127.0.0.1",
"server_port": 7890
}
},
"route_address": [
"0.0.0.0/1",
"128.0.0.0/1",
"::/1",
"8000::/1"
],
"route_exclude_address": [],
"stack": "mixed",
"tag": "tun-in",
"type": "tun"
}
],
"log": {
"level": "debug"
},
"outbounds": [
{
"tag": "direct",
"type": "direct"
},
{
"method": "chacha20-ietf-poly1305",
"password": "password",
"server": "ssserver.example.com",
"server_port": 443,
"tag": "shadowsocks_ssserver",
"type": "shadowsocks"
},
{
"outbounds": [
"shadowsocks_ssserver"
],
"tag": "auto",
"type": "urltest"
},
{
"idle_timeout": "2d",
"interval": "1d",
"outbounds": [
"direct",
"shadowsocks_ssserver"
],
"tag": "bootstrap-auto",
"type": "urltest",
"url": "https://gh-proxy.com/github.com/MetaCubeX/meta-rules-dat/raw/refs/heads/sing/geo-lite/geoip/apple.srs"
},
{
"default": "shadowsocks_ssserver",
"outbounds": [
"shadowsocks_ssserver",
"auto"
],
"tag": "proxy",
"type": "selector"
},
{
"default": "proxy",
"outbounds": [
"proxy",
"auto",
"direct",
"shadowsocks_ssserver"
],
"tag": "final",
"type": "selector"
}
],
"route": {
"auto_detect_interface": true,
"default_domain_resolver": {
"server": "domestic"
},
"final": "final",
"rule_set": [
{
"download_detour": "bootstrap-auto",
"format": "binary",
"tag": "geosite-ads",
"type": "remote",
"url": "https://gh-proxy.com/github.com/MetaCubeX/meta-rules-dat/raw/refs/heads/sing/geo/geosite/category-ads.srs"
},
{
"download_detour": "bootstrap-auto",
"format": "binary",
"tag": "geoip-cn",
"type": "remote",
"url": "https://gh-proxy.com/github.com/MetaCubeX/meta-rules-dat/raw/refs/heads/sing/geo/geoip/cn.srs"
},
{
"download_detour": "bootstrap-auto",
"format": "binary",
"tag": "geosite-cn",
"type": "remote",
"url": "https://gh-proxy.com/github.com/MetaCubeX/meta-rules-dat/raw/refs/heads/sing/geo/geosite/cn.srs"
}
],
"rules": [
{
"action": "sniff",
"sniffer": [
"http",
"tls",
"quic",
"dns"
]
},
{
"action": "hijack-dns",
"protocol": "dns"
},
{
"action": "hijack-dns",
"port": 53
},
{
"ip_cidr": "100.64.0.0/10",
"outbound": "ts-endpoint"
},
{
"ip_is_private": true,
"outbound": "direct"
},
{
"outbound": "direct",
"rule_set": "geoip-cn"
},
{
"outbound": "direct",
"rule_set": "geosite-cn"
},
{
"action": "reject",
"rule_set": "geosite-ads"
},
{
"action": "resolve"
}
]
}
}
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in protocol/tailscale/dns_transport.go around lines 241-263, then review how the Tailscale DNS search domain is obtained and how single-label queries are handled. Done means a single-label query such as abc resolves through the tailnet search domain while fully qualified queries continue to work; the payload names no test file.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- networking
- Issue type
- Feature
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 62/100