From fb3f25c344872b5684a2fd65624c0831077baa69 Mon Sep 17 00:00:00 2001 From: mzz2017 <2017@duck.com> Date: Sun, 5 Feb 2023 13:05:28 +0800 Subject: [PATCH] fix: dns problem --- component/control/dns.go | 64 ++++++++++++++++++++++++++++------------ component/control/udp.go | 23 ++++++++------- 2 files changed, 57 insertions(+), 30 deletions(-) diff --git a/component/control/dns.go b/component/control/dns.go index 864d9e2..ca3c794 100644 --- a/component/control/dns.go +++ b/component/control/dns.go @@ -177,31 +177,32 @@ func EnsureAdditionalOpt(dm *dnsmessage.Message, isReqAdd bool) (bool, error) { return false, nil } +type RscWrapper struct { + Rsc dnsmessage.Resource +} + +func (w RscWrapper) String() string { + return fmt.Sprintf("%v: %v", w.Rsc.Header.GoString(), w.Rsc.Body.GoString()) +} +func FormatDnsRsc(ans []dnsmessage.Resource) (w []string) { + for _, a := range ans { + w = append(w, RscWrapper{Rsc: a}.String()) + } + return w +} + // DnsRespHandler handle DNS resp. This function should be invoked when cache miss. -func (c *ControlPlane) DnsRespHandler(data []byte) (newData []byte, err error) { +func (c *ControlPlane) DnsRespHandler(data []byte, validateRushAns bool) (newData []byte, err error) { var msg dnsmessage.Message if err = msg.Unpack(data); err != nil { return nil, fmt.Errorf("unpack dns pkt: %w", err) } - defer func() { - if err == nil { - exist, e := EnsureAdditionalOpt(&msg, false) - if e != nil && !errors.Is(e, UnsupportedQuestionTypeError) { - c.log.Warnf("EnsureAdditionalOpt: %v", e) - } - if e == nil && !exist { - // Additional record OPT in the request was ensured, and in normal case the resp should also set it. - // This DNS packet may be a rush-answer, and we should reject it. - // Note that additional record OPT may not be supported by home router either. - err = SuspectedRushAnswerError - } - } - }() - FlipDnsQuestionCase(&msg) // Check healthy resp. - if !msg.Response || msg.RCode != dnsmessage.RCodeSuccess || len(msg.Questions) == 0 { + if !msg.Response || len(msg.Questions) == 0 { return data, nil } + + FlipDnsQuestionCase(&msg) q := msg.Questions[0] // Align Name. if len(msg.Answers) > 0 && @@ -213,12 +214,22 @@ func (c *ControlPlane) DnsRespHandler(data []byte) (newData []byte, err error) { msg.Additionals[i].Header.Name.Data = q.Name.Data } } + for i := range msg.Authorities { + if strings.EqualFold(msg.Authorities[i].Header.Name.String(), q.Name.String()) { + msg.Authorities[i].Header.Name.Data = q.Name.Data + } + } + + // Check suc resp. + if msg.RCode != dnsmessage.RCodeSuccess { + return msg.Pack() + } // Check req type. switch q.Type { case dnsmessage.TypeA, dnsmessage.TypeAAAA: default: - return data, nil + return msg.Pack() } // Set ttl. @@ -244,10 +255,25 @@ func (c *ControlPlane) DnsRespHandler(data []byte) (newData []byte, err error) { return msg.Pack() } + if validateRushAns { + exist, e := EnsureAdditionalOpt(&msg, false) + if e != nil && !errors.Is(e, UnsupportedQuestionTypeError) { + c.log.Warnf("EnsureAdditionalOpt: %v", e) + } + if e == nil && !exist { + // Additional record OPT in the request was ensured, and in normal case the resp should also set it. + // This DNS packet may be a rush-answer, and we should reject it. + return nil, SuspectedRushAnswerError + } + } + // Update dnsCache. c.log.WithFields(logrus.Fields{ "qname": q.Name, - "ans": msg.Answers, + "rcode": msg.RCode, + "ans": FormatDnsRsc(msg.Answers), + "auth": FormatDnsRsc(msg.Authorities), + "addi": FormatDnsRsc(msg.Additionals), }).Tracef("Update DNS record cache") c.mutex.Lock() fqdn := strings.ToLower(q.Name.String()) diff --git a/component/control/udp.go b/component/control/udp.go index e140b8b..ccd54b7 100644 --- a/component/control/udp.go +++ b/component/control/udp.go @@ -80,26 +80,23 @@ func sendPktWithHdr(data []byte, from netip.AddrPort, lConn *net.UDPConn, to net return err } -func (c *ControlPlane) RelayToUDP(lConn *net.UDPConn, to netip.AddrPort, isDNS bool, dummyFrom *netip.AddrPort) UdpHandler { +func (c *ControlPlane) RelayToUDP(lConn *net.UDPConn, to netip.AddrPort, isDNS bool, dummyFrom *netip.AddrPort, validateRushAns bool) UdpHandler { return func(data []byte, from netip.AddrPort) (err error) { + // Do not return conn-unrelated err in this func. + if isDNS { - data, err = c.DnsRespHandler(data) + data, err = c.DnsRespHandler(data, validateRushAns) if err != nil { if errors.Is(err, SuspectedRushAnswerError) { - if from.Addr().IsPrivate() { - // Additional record OPT may not be supported by home router. - // And we should trust home devices even if they make rush-answer. - c.log.Tracef("DnsRespHandler: received %v", err) - err = nil - goto sendToClient - } // Reject DNS rush-answer. return err } c.log.Debugf("DnsRespHandler: %v", err) + if data == nil { + return nil + } } } - sendToClient: if dummyFrom != nil { from = *dummyFrom } @@ -163,8 +160,12 @@ func (c *ControlPlane) handlePkt(data []byte, lConn *net.UDPConn, lAddrPort neti } } + // We only validate rush-ans when outbound is direct and pkt does not send to a home device. + // Because additional record OPT may not be supported by home router. + // So se should trust home devices even if they make rush-answer (or looks like). + validateRushAns := addrHdr.Outbound == uint8(consts.OutboundDirect) && !dest.Addr().IsPrivate() ue, err := DefaultUdpEndpointPool.GetOrCreate(lAddrPort, &UdpEndpointOptions{ - Handler: c.RelayToUDP(lConn, lAddrPort, isDns, dummyFrom), + Handler: c.RelayToUDP(lConn, lAddrPort, isDns, dummyFrom, validateRushAns), NatTimeout: natTimeout, DialerFunc: func() (*dialer.Dialer, error) { newDialer, err := outbound.Select()