From 48cc8880ebdb91f39529ffdb19367ca58a2c67aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 21 Apr 2026 14:41:02 +0800 Subject: [PATCH] Fix Tailscale search domain response name mismatch --- protocol/tailscale/dns_transport.go | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/protocol/tailscale/dns_transport.go b/protocol/tailscale/dns_transport.go index 8d5cad59b..932346eb2 100644 --- a/protocol/tailscale/dns_transport.go +++ b/protocol/tailscale/dns_transport.go @@ -234,11 +234,13 @@ func (t *DNSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.M } func (t *DNSTransport) exchangeWithSearchDomains(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) { - singleLabel := strings.TrimSuffix(message.Question[0].Name, ".") + originalQuestion := message.Question[0] + singleLabel := strings.TrimSuffix(originalQuestion.Name, ".") var lastErr error for _, searchDomain := range t.searchDomains { - question := message.Question[0] - question.Name = singleLabel + "." + searchDomain + expandedName := singleLabel + "." + searchDomain + question := originalQuestion + question.Name = expandedName rewritten := *message rewritten.Question = []mDNS.Question{question} response, err := t.exchangeOnce(ctx, &rewritten, false) @@ -246,6 +248,7 @@ func (t *DNSTransport) exchangeWithSearchDomains(ctx context.Context, message *m if response.Rcode == mDNS.RcodeNameError { continue } + restoreOriginalQuestion(response, expandedName, originalQuestion) return response, nil } if errors.Is(err, dns.RcodeNameError) { @@ -259,6 +262,17 @@ func (t *DNSTransport) exchangeWithSearchDomains(ctx context.Context, message *m return nil, dns.RcodeNameError } +// RFC 1035 ยง4.1.1 requires the response Question to match the request byte-for-byte, +// and stub resolvers discard Answer RRs whose owner name does not match the question. +func restoreOriginalQuestion(response *mDNS.Msg, expandedName string, originalQuestion mDNS.Question) { + response.Question = []mDNS.Question{originalQuestion} + for _, rr := range response.Answer { + if strings.EqualFold(rr.Header().Name, expandedName) { + rr.Header().Name = originalQuestion.Name + } + } +} + func (t *DNSTransport) exchangeOnce(ctx context.Context, message *mDNS.Msg, allowDefaultResolvers bool) (*mDNS.Msg, error) { question := message.Question[0] addresses, hostsLoaded := t.hosts[question.Name]