From 7381fb19fdff90908c36e0ec4318f033d7cf5ad7 Mon Sep 17 00:00:00 2001 From: Erik Geiser Date: Fri, 7 Jun 2024 15:53:52 +0200 Subject: [PATCH] Delegate DNS queries with the same protocol as the incoming query --- dns.go | 18 ++++++------- dns_test.go | 70 +++++++++++++++++++++++++++++++++++++++++--------- filter_test.go | 2 +- 3 files changed, 67 insertions(+), 23 deletions(-) diff --git a/dns.go b/dns.go index a2d6896..676aac9 100644 --- a/dns.go +++ b/dns.go @@ -65,7 +65,8 @@ func createDNSReplyFromRequest( shouldRespond, reason := shouldRespondToNameResolutionQuery(config, name, q.Qtype, peer, peerHostnames) if !shouldRespond { - answers := handleIgnored(logger, q, name, queryType(q, request.Opcode), peer, reason, handlerType, delegateQuestion) + answers := handleIgnored(logger, q, name, queryType(q, request.Opcode), + peer, reason, handlerType, rw.RemoteAddr().Network(), delegateQuestion) reply.Answer = append(reply.Answer, answers...) continue @@ -133,7 +134,7 @@ func createDNSReplyFromRequest( }) default: answers := handleIgnored(logger, q, name, queryType(q, request.Opcode), peer, IgnoreReasonQueryTypeUnhandled, - handlerType, delegateQuestion) + handlerType, rw.RemoteAddr().Network(), delegateQuestion) reply.Answer = append(reply.Answer, answers...) continue @@ -156,11 +157,11 @@ func createDNSReplyFromRequest( func handleIgnored( logger *Logger, rawQuestion dns.Question, name string, queryType string, peer net.IP, reason string, - handlerType HandlerType, delegateQuestion delegateQuestionFunc, + handlerType HandlerType, net string, delegateQuestion delegateQuestionFunc, ) []dns.RR { switch { case handlerType == HandlerTypeDNS && delegateQuestion != nil: - rr, err := delegateQuestion(rawQuestion) + rr, err := delegateQuestion(rawQuestion, net) if err != nil { logger.Errorf("cannot delegate %s query for %q from %s: %v", queryType, rawQuestion.Name, peer, err) } @@ -415,18 +416,15 @@ func hasSpecificIPv4Address(iface *net.Interface, ip net.IP) bool { return false } -type delegateQuestionFunc func(dns.Question) ([]dns.RR, error) +type delegateQuestionFunc func(q dns.Question, net string) ([]dns.RR, error) func delegateToDNSServer(dnsServer string, timeout time.Duration) delegateQuestionFunc { - return func(q dns.Question) ([]dns.RR, error) { + return func(q dns.Question, net string) ([]dns.RR, error) { c := &dns.Client{ Timeout: timeout, } - if q.Qtype == dns.TypeANY || q.Qtype == dns.TypeTXT { - c.Net = "tcp" - } - + c.Net = net m1 := new(dns.Msg) m1.Id = dns.Id() m1.RecursionDesired = true diff --git a/dns_test.go b/dns_test.go index 27123cf..ff74b72 100644 --- a/dns_test.go +++ b/dns_test.go @@ -246,13 +246,14 @@ func TestDNSDelegation(t *testing.T) { SpoofFor: []*hostMatcher{newHostMatcher("10.0.0.99", defaultLookupTimeout)}, } - reply := createDNSReplyFromRequest(mockRW, aQuery, nil, cfg, HandlerTypeDNS, func(q dns.Question) ([]dns.RR, error) { - if q.Qtype == dns.TypeA && q.Name == "host" { - return []dns.RR{&dns.A{Hdr: rrHeader(q.Name, dns.TypeA, 1*time.Second), A: delegatedResponseIP}}, nil - } + reply := createDNSReplyFromRequest(mockRW, aQuery, nil, cfg, HandlerTypeDNS, + func(q dns.Question, net string) ([]dns.RR, error) { + if q.Qtype == dns.TypeA && q.Name == "host" { + return []dns.RR{&dns.A{Hdr: rrHeader(q.Name, dns.TypeA, 1*time.Second), A: delegatedResponseIP}}, nil + } - return nil, nil - }) + return nil, nil + }) if reply == nil { t.Fatalf("no delegated reply") } @@ -306,13 +307,14 @@ func TestDelegatedUnhandledQuery(t *testing.T) { SpoofFor: []*hostMatcher{newHostMatcher("10.0.0.99", defaultLookupTimeout)}, } - reply := createDNSReplyFromRequest(mockRW, aQuery, nil, cfg, HandlerTypeDNS, func(q dns.Question) ([]dns.RR, error) { - if q.Qtype == dns.TypePTR && q.Name == "host" { - return []dns.RR{&dns.PTR{Hdr: rrHeader(q.Name, dns.TypePTR, 1*time.Second), Ptr: delegatedPTR}}, nil - } + reply := createDNSReplyFromRequest(mockRW, aQuery, nil, cfg, HandlerTypeDNS, + func(q dns.Question, net string) ([]dns.RR, error) { + if q.Qtype == dns.TypePTR && q.Name == "host" { + return []dns.RR{&dns.PTR{Hdr: rrHeader(q.Name, dns.TypePTR, 1*time.Second), Ptr: delegatedPTR}}, nil + } - return nil, nil - }) + return nil, nil + }) if reply == nil { t.Fatalf("no delegated reply") } @@ -331,6 +333,50 @@ func TestDelegatedUnhandledQuery(t *testing.T) { } } +func TestDelegatedQueryUDP(t *testing.T) { + aQuery := &dns.Msg{} + aQuery.SetQuestion("host", dns.TypeANY) + + relayIPv4 := mustParseIP(t, "10.0.0.2") + mockRW := mockResonseWriter{Remote: &net.UDPAddr{IP: mustParseIP(t, "10.0.0.1")}} + + cfg := Config{RelayIPv4: relayIPv4} + + reply := createDNSReplyFromRequest(mockRW, aQuery, nil, cfg, HandlerTypeDNS, + func(q dns.Question, net string) ([]dns.RR, error) { + if net != "udp" { + t.Fatalf("UDP query was delegated via %q", net) + } + + return []dns.RR{&dns.ANY{Hdr: rrHeader(q.Name, dns.TypeANY, 1*time.Second)}}, nil + }) + if reply == nil { + t.Fatalf("no delegated reply") + } +} + +func TestDelegatedQueryTCP(t *testing.T) { + aQuery := &dns.Msg{} + aQuery.SetQuestion("host", dns.TypeA) + + relayIPv4 := mustParseIP(t, "10.0.0.2") + mockRW := mockResonseWriter{Remote: &net.TCPAddr{IP: mustParseIP(t, "10.0.0.1")}} + + cfg := Config{RelayIPv4: relayIPv4} + + reply := createDNSReplyFromRequest(mockRW, aQuery, nil, cfg, HandlerTypeDNS, + func(q dns.Question, net string) ([]dns.RR, error) { + if net != "tcp" { + t.Fatalf("TCP query was delegated via %q", net) + } + + return []dns.RR{&dns.A{Hdr: rrHeader(q.Name, dns.TypeA, 1*time.Second), A: relayIPv4}}, nil + }) + if reply == nil { + t.Fatalf("no delegated reply") + } +} + func testReply(tb testing.TB, requestFileName string, replyFileName string) { tb.Helper() diff --git a/filter_test.go b/filter_test.go index 27cdd6e..78297a5 100644 --- a/filter_test.go +++ b/filter_test.go @@ -505,7 +505,7 @@ func TestFilterDHCP(t *testing.T) { }, } - hostMatcherLookupFunction = func(host string, timeout time.Duration) ([]net.IP, error) { + hostMatcherLookupFunction = func(host string, _ time.Duration) ([]net.IP, error) { switch host { case "somehost": return []net.IP{mustParseIP(t, "192.168.0.5")}, nil