fix(dns): should reject with nx instead of 0.0.0.0 (#141)

This commit is contained in:
mzz
2023-07-16 11:28:28 +08:00
committed by GitHub
parent e5e07015ce
commit 00b39df677
3 changed files with 99 additions and 134 deletions

View File

@ -409,11 +409,12 @@ func NewControlPlane(
} }
return nil return nil
}, },
NewCache: func(fqdn string, answers []dnsmessage.RR, deadline time.Time) (cache *DnsCache, err error) { NewCache: func(fqdn string, answers []dnsmessage.RR, deadline time.Time, originalDeadline time.Time) (cache *DnsCache, err error) {
return &DnsCache{ return &DnsCache{
DomainBitmap: plane.routingMatcher.domainMatcher.MatchDomainBitmap(fqdn), DomainBitmap: plane.routingMatcher.domainMatcher.MatchDomainBitmap(fqdn),
Answer: answers, Answer: answers,
Deadline: deadline, Deadline: deadline,
OriginalDeadline: originalDeadline,
}, nil }, nil
}, },
BestDialerChooser: plane.chooseBestDnsDialer, BestDialerChooser: plane.chooseBestDnsDialer,
@ -423,7 +424,9 @@ func NewControlPlane(
return nil, err return nil, err
} }
// Refresh domain routing cache with new routing. // Refresh domain routing cache with new routing.
if dnsCache != nil && len(dnsCache) > 0 { // FIXME: We temperarily disable it because we want to make change of DNS section take effects immediately.
// TODO: Add change detection.
if false && len(dnsCache) > 0 {
for cacheKey, cache := range dnsCache { for cacheKey, cache := range dnsCache {
// Also refresh out-dated routing because kernel map items have no expiration. // Also refresh out-dated routing because kernel map items have no expiration.
lastDot := strings.LastIndex(cacheKey, ".") lastDot := strings.LastIndex(cacheKey, ".")
@ -556,7 +559,7 @@ func (c *ControlPlane) ChooseDialTarget(outbound consts.OutboundIndex, dst netip
if !outbound.IsReserved() && domain != "" { if !outbound.IsReserved() && domain != "" {
switch c.dialMode { switch c.dialMode {
case consts.DialMode_Domain: case consts.DialMode_Domain:
if cache := c.dnsController.LookupDnsRespCache(domain, common.AddrToDnsType(dst.Addr())); cache != nil { if cache := c.dnsController.LookupDnsRespCache(c.dnsController.cacheKey(domain, common.AddrToDnsType(dst.Addr())), true); cache != nil {
// Has A/AAAA records. It is a real domain. // Has A/AAAA records. It is a real domain.
dialMode = consts.DialMode_Domain dialMode = consts.DialMode_Domain
} else { } else {

View File

@ -14,9 +14,10 @@ import (
) )
type DnsCache struct { type DnsCache struct {
DomainBitmap []uint32 DomainBitmap []uint32
Answer []dnsmessage.RR Answer []dnsmessage.RR
Deadline time.Time Deadline time.Time
OriginalDeadline time.Time // This field is not impacted by `fixed_domain_ttl`.
} }
func (c *DnsCache) FillInto(req *dnsmessage.Msg) { func (c *DnsCache) FillInto(req *dnsmessage.Msg) {

View File

@ -59,7 +59,7 @@ type DnsControllerOption struct {
Log *logrus.Logger Log *logrus.Logger
CacheAccessCallback func(cache *DnsCache) (err error) CacheAccessCallback func(cache *DnsCache) (err error)
CacheRemoveCallback func(cache *DnsCache) (err error) CacheRemoveCallback func(cache *DnsCache) (err error)
NewCache func(fqdn string, answers []dnsmessage.RR, deadline time.Time) (cache *DnsCache, err error) NewCache func(fqdn string, answers []dnsmessage.RR, deadline time.Time, originalDeadline time.Time) (cache *DnsCache, err error)
BestDialerChooser func(req *udpRequest, upstream *dns.Upstream) (*dialArgument, error) BestDialerChooser func(req *udpRequest, upstream *dns.Upstream) (*dialArgument, error)
IpVersionPrefer int IpVersionPrefer int
FixedDomainTtl map[string]int FixedDomainTtl map[string]int
@ -74,7 +74,7 @@ type DnsController struct {
log *logrus.Logger log *logrus.Logger
cacheAccessCallback func(cache *DnsCache) (err error) cacheAccessCallback func(cache *DnsCache) (err error)
cacheRemoveCallback func(cache *DnsCache) (err error) cacheRemoveCallback func(cache *DnsCache) (err error)
newCache func(fqdn string, answers []dnsmessage.RR, deadline time.Time) (cache *DnsCache, err error) newCache func(fqdn string, answers []dnsmessage.RR, deadline time.Time, originalDeadline time.Time) (cache *DnsCache, err error)
bestDialerChooser func(req *udpRequest, upstream *dns.Upstream) (*dialArgument, error) bestDialerChooser func(req *udpRequest, upstream *dns.Upstream) (*dialArgument, error)
fixedDomainTtl map[string]int fixedDomainTtl map[string]int
@ -124,37 +124,42 @@ func (c *DnsController) cacheKey(qname string, qtype uint16) string {
return dnsmessage.CanonicalName(qname) + strconv.Itoa(int(qtype)) return dnsmessage.CanonicalName(qname) + strconv.Itoa(int(qtype))
} }
func (c *DnsController) RemoveDnsRespCache(qname string, qtype uint16) { func (c *DnsController) RemoveDnsRespCache(cacheKey string) {
c.dnsCacheMu.Lock() c.dnsCacheMu.Lock()
key := c.cacheKey(qname, qtype) _, ok := c.dnsCache[cacheKey]
_, ok := c.dnsCache[key]
if ok { if ok {
delete(c.dnsCache, key) delete(c.dnsCache, cacheKey)
} }
c.dnsCacheMu.Unlock() c.dnsCacheMu.Unlock()
} }
func (c *DnsController) LookupDnsRespCache(qname string, qtype uint16) (cache *DnsCache) { func (c *DnsController) LookupDnsRespCache(cacheKey string, ignoreFixedTtl bool) (cache *DnsCache) {
c.dnsCacheMu.Lock() c.dnsCacheMu.Lock()
cache, ok := c.dnsCache[c.cacheKey(qname, qtype)] cache, ok := c.dnsCache[cacheKey]
c.dnsCacheMu.Unlock() c.dnsCacheMu.Unlock()
if !ok {
return nil
}
var deadline time.Time
if !ignoreFixedTtl {
deadline = cache.Deadline
} else {
deadline = cache.OriginalDeadline
}
// We should make sure the cache did not expire, or // We should make sure the cache did not expire, or
// return nil and request a new lookup to refresh the cache. // return nil and request a new lookup to refresh the cache.
if ok && cache.Deadline.After(time.Now()) { if !deadline.After(time.Now()) {
return cache return nil
} }
return nil if err := c.cacheAccessCallback(cache); err != nil {
c.log.Warnf("failed to BatchUpdateDomainRouting: %v", err)
return nil
}
return cache
} }
// LookupDnsRespCache_ will modify the msg in place. // LookupDnsRespCache_ will modify the msg in place.
func (c *DnsController) LookupDnsRespCache_(msg *dnsmessage.Msg) (resp []byte) { func (c *DnsController) LookupDnsRespCache_(msg *dnsmessage.Msg, cacheKey string, ignoreFixedTtl bool) (resp []byte) {
if len(msg.Question) == 0 { cache := c.LookupDnsRespCache(cacheKey, ignoreFixedTtl)
return nil
}
q := msg.Question[0]
if msg.Response {
return nil
}
cache := c.LookupDnsRespCache(q.Name, q.Qtype)
if cache != nil { if cache != nil {
cache.FillInto(msg) cache.FillInto(msg)
b, err := msg.Pack() b, err := msg.Pack()
@ -162,31 +167,23 @@ func (c *DnsController) LookupDnsRespCache_(msg *dnsmessage.Msg) (resp []byte) {
c.log.Warnf("failed to pack: %v", err) c.log.Warnf("failed to pack: %v", err)
return nil return nil
} }
if err = c.cacheAccessCallback(cache); err != nil {
c.log.Warnf("failed to BatchUpdateDomainRouting: %v", err)
return nil
}
return b return b
} }
return nil return nil
} }
// DnsRespHandler handle DNS resp. // NormalizeAndCacheDnsResp_ handle DNS resp in place.
func (c *DnsController) DnsRespHandler(data []byte) (newMsg *dnsmessage.Msg, err error) { func (c *DnsController) NormalizeAndCacheDnsResp_(msg *dnsmessage.Msg) (err error) {
var msg dnsmessage.Msg
if err = msg.Unpack(data); err != nil {
return nil, fmt.Errorf("unpack dns pkt: %w", err)
}
// Check healthy resp. // Check healthy resp.
if !msg.Response || len(msg.Question) == 0 { if !msg.Response || len(msg.Question) == 0 {
return &msg, nil return nil
} }
q := msg.Question[0] q := msg.Question[0]
// Check suc resp. // Check suc resp.
if msg.Rcode != dnsmessage.RcodeSuccess { if msg.Rcode != dnsmessage.RcodeSuccess {
return &msg, nil return nil
} }
// Get TTL. // Get TTL.
@ -207,10 +204,10 @@ func (c *DnsController) DnsRespHandler(data []byte) (newMsg *dnsmessage.Msg, err
case dnsmessage.TypeA, dnsmessage.TypeAAAA: case dnsmessage.TypeA, dnsmessage.TypeAAAA:
default: default:
// Update DnsCache. // Update DnsCache.
if err = c.updateDnsCache(&msg, ttl, &q); err != nil { if err = c.updateDnsCache(msg, ttl, &q); err != nil {
return nil, err return err
} }
return &msg, nil return nil
} }
// Set ttl. // Set ttl.
@ -232,18 +229,18 @@ loop:
} }
if !reqIpRecord { if !reqIpRecord {
// Update DnsCache. // Update DnsCache.
if err = c.updateDnsCache(&msg, ttl, &q); err != nil { if err = c.updateDnsCache(msg, ttl, &q); err != nil {
return nil, err return err
} }
return &msg, nil return nil
} }
// Update DnsCache. // Update DnsCache.
if err = c.updateDnsCache(&msg, ttl, &q); err != nil { if err = c.updateDnsCache(msg, ttl, &q); err != nil {
return nil, err return err
} }
// Pack to get newData. // Pack to get newData.
return &msg, nil return nil
} }
func (c *DnsController) updateDnsCache(msg *dnsmessage.Msg, ttl uint32, q *dnsmessage.Question) error { func (c *DnsController) updateDnsCache(msg *dnsmessage.Msg, ttl uint32, q *dnsmessage.Question) error {
@ -262,7 +259,9 @@ func (c *DnsController) updateDnsCache(msg *dnsmessage.Msg, ttl uint32, q *dnsme
return nil return nil
} }
func (c *DnsController) __updateDnsCacheDeadline(host string, dnsTyp uint16, answers []dnsmessage.RR, deadlineFunc func(now time.Time, host string) time.Time) (err error) { type daedlineFunc func(now time.Time, host string) (deadline time.Time, originalDeadline time.Time)
func (c *DnsController) __updateDnsCacheDeadline(host string, dnsTyp uint16, answers []dnsmessage.RR, deadlineFunc daedlineFunc) (err error) {
var fqdn string var fqdn string
if strings.HasSuffix(host, ".") { if strings.HasSuffix(host, ".") {
fqdn = strings.ToLower(host) fqdn = strings.ToLower(host)
@ -276,7 +275,7 @@ func (c *DnsController) __updateDnsCacheDeadline(host string, dnsTyp uint16, ans
} }
now := time.Now() now := time.Now()
deadline := deadlineFunc(now, host) deadline, originalDeadline := deadlineFunc(now, host)
cacheKey := c.cacheKey(fqdn, dnsTyp) cacheKey := c.cacheKey(fqdn, dnsTyp)
c.dnsCacheMu.Lock() c.dnsCacheMu.Lock()
@ -284,9 +283,10 @@ func (c *DnsController) __updateDnsCacheDeadline(host string, dnsTyp uint16, ans
if ok { if ok {
cache.Answer = answers cache.Answer = answers
cache.Deadline = deadline cache.Deadline = deadline
cache.OriginalDeadline = originalDeadline
c.dnsCacheMu.Unlock() c.dnsCacheMu.Unlock()
} else { } else {
cache, err = c.newCache(fqdn, answers, deadline) cache, err = c.newCache(fqdn, answers, deadline, originalDeadline)
if err != nil { if err != nil {
c.dnsCacheMu.Unlock() c.dnsCacheMu.Unlock()
return err return err
@ -302,42 +302,29 @@ func (c *DnsController) __updateDnsCacheDeadline(host string, dnsTyp uint16, ans
} }
func (c *DnsController) UpdateDnsCacheDeadline(host string, dnsTyp uint16, answers []dnsmessage.RR, deadline time.Time) (err error) { func (c *DnsController) UpdateDnsCacheDeadline(host string, dnsTyp uint16, answers []dnsmessage.RR, deadline time.Time) (err error) {
return c.__updateDnsCacheDeadline(host, dnsTyp, answers, func(now time.Time, host string) time.Time { return c.__updateDnsCacheDeadline(host, dnsTyp, answers, func(now time.Time, host string) (daedline time.Time, originalDeadline time.Time) {
if fixedTtl, ok := c.fixedDomainTtl[host]; ok { if fixedTtl, ok := c.fixedDomainTtl[host]; ok {
/// NOTICE: Cannot set TTL accurately. /// NOTICE: Cannot set TTL accurately.
if now.Sub(deadline).Seconds() > float64(fixedTtl) { if now.Sub(deadline).Seconds() > float64(fixedTtl) {
return now.Add(time.Duration(fixedTtl) * time.Second) deadline := now.Add(time.Duration(fixedTtl) * time.Second)
return deadline, deadline
} }
} }
return deadline return deadline, deadline
}) })
} }
func (c *DnsController) UpdateDnsCacheTtl(host string, dnsTyp uint16, answers []dnsmessage.RR, ttl int) (err error) { func (c *DnsController) UpdateDnsCacheTtl(host string, dnsTyp uint16, answers []dnsmessage.RR, ttl int) (err error) {
return c.__updateDnsCacheDeadline(host, dnsTyp, answers, func(now time.Time, host string) time.Time { return c.__updateDnsCacheDeadline(host, dnsTyp, answers, func(now time.Time, host string) (daedline time.Time, originalDeadline time.Time) {
originalDeadline = now.Add(time.Duration(ttl) * time.Second)
if fixedTtl, ok := c.fixedDomainTtl[host]; ok { if fixedTtl, ok := c.fixedDomainTtl[host]; ok {
return now.Add(time.Duration(fixedTtl) * time.Second) return now.Add(time.Duration(fixedTtl) * time.Second), originalDeadline
} else { } else {
return now.Add(time.Duration(ttl) * time.Second) return originalDeadline, originalDeadline
} }
}) })
} }
func (c *DnsController) DnsRespHandlerFactory() func(data []byte, from netip.AddrPort) (msg *dnsmessage.Msg, err error) {
return func(data []byte, from netip.AddrPort) (msg *dnsmessage.Msg, err error) {
// Do not return conn-unrelated err in this func.
msg, err = c.DnsRespHandler(data)
if err != nil {
if c.log.IsLevelEnabled(logrus.DebugLevel) {
c.log.Debugf("DnsRespHandler: %v", err)
}
return nil, err
}
return msg, nil
}
}
type udpRequest struct { type udpRequest struct {
lanWanFlag consts.LanWanFlag lanWanFlag consts.LanWanFlag
realSrc netip.AddrPort realSrc netip.AddrPort
@ -412,8 +399,7 @@ func (c *DnsController) Handle_(dnsMessage *dnsmessage.Msg, req *udpRequest) (er
} }
// Join results and consider whether to response. // Join results and consider whether to response.
dnsMessage.Response = false resp := c.LookupDnsRespCache_(dnsMessage, c.cacheKey(qname, qtype), true)
resp := c.LookupDnsRespCache_(dnsMessage)
if resp == nil { if resp == nil {
// resp is not valid. // resp is not valid.
c.log.WithFields(logrus.Fields{ c.log.WithFields(logrus.Fields{
@ -422,7 +408,7 @@ func (c *DnsController) Handle_(dnsMessage *dnsmessage.Msg, req *udpRequest) (er
return c.sendReject_(dnsMessage, req) return c.sendReject_(dnsMessage, req)
} }
// resp is valid. // resp is valid.
cache2 := c.LookupDnsRespCache(qname, qtype2) cache2 := c.LookupDnsRespCache(c.cacheKey(qname, qtype2), true)
if c.qtypePrefer == qtype || cache2 == nil || !cache2.IncludeAnyIp() { if c.qtypePrefer == qtype || cache2 == nil || !cache2.IncludeAnyIp() {
return sendPkt(resp, req.realDst, req.realSrc, req.src, req.lConn, req.lanWanFlag) return sendPkt(resp, req.realDst, req.realSrc, req.src, req.lConn, req.lanWanFlag)
} else { } else {
@ -450,19 +436,22 @@ func (c *DnsController) handle_(
return err return err
} }
cacheKey := c.cacheKey(qname, qtype)
if upstreamIndex == consts.DnsRequestOutboundIndex_Reject { if upstreamIndex == consts.DnsRequestOutboundIndex_Reject {
// Reject with empty answer. // Reject with empty answer.
c.RemoveDnsRespCache(qname, qtype) c.RemoveDnsRespCache(cacheKey)
return c.sendReject_(dnsMessage, req) return c.sendReject_(dnsMessage, req)
} }
// No parallel for the same lookup. // No parallel for the same lookup.
_mu, _ := c.handling.LoadOrStore(c.cacheKey(qname, qtype), new(sync.Mutex)) _mu, _ := c.handling.LoadOrStore(cacheKey, new(sync.Mutex))
mu := _mu.(*sync.Mutex) mu := _mu.(*sync.Mutex)
mu.Lock() mu.Lock()
defer mu.Unlock() defer mu.Unlock()
defer c.handling.Delete(cacheKey)
if resp := c.LookupDnsRespCache_(dnsMessage); resp != nil { if resp := c.LookupDnsRespCache_(dnsMessage, cacheKey, false); resp != nil {
// Send cache to client directly. // Send cache to client directly.
if needResp { if needResp {
if err = sendPkt(resp, req.realDst, req.realSrc, req.src, req.lConn, req.lanWanFlag); err != nil { if err = sendPkt(resp, req.realDst, req.realSrc, req.src, req.lConn, req.lanWanFlag); err != nil {
@ -500,31 +489,6 @@ func (c *DnsController) handle_(
// sendReject_ send empty answer. // sendReject_ send empty answer.
func (c *DnsController) sendReject_(dnsMessage *dnsmessage.Msg, req *udpRequest) (err error) { func (c *DnsController) sendReject_(dnsMessage *dnsmessage.Msg, req *udpRequest) (err error) {
dnsMessage.Answer = nil dnsMessage.Answer = nil
if len(dnsMessage.Question) > 0 {
q := dnsMessage.Question[0]
switch typ := q.Qtype; typ {
case dnsmessage.TypeA:
dnsMessage.Answer = []dnsmessage.RR{&dnsmessage.A{
Hdr: dnsmessage.RR_Header{
Name: q.Name,
Rrtype: typ,
Class: dnsmessage.ClassINET,
Ttl: 0,
},
A: UnspecifiedAddressA.AsSlice(),
}}
case dnsmessage.TypeAAAA:
dnsMessage.Answer = []dnsmessage.RR{&dnsmessage.AAAA{
Hdr: dnsmessage.RR_Header{
Name: q.Name,
Rrtype: typ,
Class: dnsmessage.ClassINET,
Ttl: 0,
},
AAAA: UnspecifiedAddressAAAA.AsSlice(),
}}
}
}
dnsMessage.Rcode = dnsmessage.RcodeSuccess dnsMessage.Rcode = dnsmessage.RcodeSuccess
dnsMessage.Response = true dnsMessage.Response = true
dnsMessage.RecursionAvailable = true dnsMessage.RecursionAvailable = true
@ -582,8 +546,6 @@ func (c *DnsController) dialSend(invokingDepth int, req *udpRequest, data []byte
IsDns: true, // UDP relies on DNS check result. IsDns: true, // UDP relies on DNS check result.
} }
// dnsRespHandler caches dns response and check rush answers.
dnsRespHandler := c.DnsRespHandlerFactory()
// Dial and send. // Dial and send.
var respMsg *dnsmessage.Msg var respMsg *dnsmessage.Msg
// defer in a recursive call will delay Close(), thus we Close() before // defer in a recursive call will delay Close(), thus we Close() before
@ -621,7 +583,7 @@ func (c *DnsController) dialSend(invokingDepth int, req *udpRequest, data []byte
dnsReqCtx, cancelDnsReqCtx := context.WithTimeout(context.TODO(), 5*time.Second) dnsReqCtx, cancelDnsReqCtx := context.WithTimeout(context.TODO(), 5*time.Second)
defer cancelDnsReqCtx() defer cancelDnsReqCtx()
go func() { go func() {
// Send DNS request at 0, 2, 4 seconds. // Send DNS request every seconds.
for { for {
_, err = conn.Write(data) _, err = conn.Write(data)
if err != nil { if err != nil {
@ -641,7 +603,7 @@ func (c *DnsController) dialSend(invokingDepth int, req *udpRequest, data []byte
select { select {
case <-dnsReqCtx.Done(): case <-dnsReqCtx.Done():
return return
case <-time.After(2 * time.Second): case <-time.After(1 * time.Second):
} }
} }
}() }()
@ -649,21 +611,18 @@ func (c *DnsController) dialSend(invokingDepth int, req *udpRequest, data []byte
// We can block here because we are in a coroutine. // We can block here because we are in a coroutine.
respBuf := pool.GetFullCap(consts.EthernetMtu) respBuf := pool.GetFullCap(consts.EthernetMtu)
defer pool.Put(respBuf) defer pool.Put(respBuf)
for { // Wait for response.
// Wait for response. n, err := conn.Read(respBuf)
n, err := conn.Read(respBuf) if err != nil {
if err != nil { return fmt.Errorf("failed to read from: %v (dialer: %v): %w", dialArgument.bestTarget, dialArgument.bestDialer.Property().Name, err)
return fmt.Errorf("failed to read from: %v (dialer: %v): %w", dialArgument.bestTarget, dialArgument.bestDialer.Property().Name, err)
}
respMsg, err = dnsRespHandler(respBuf[:n], dialArgument.bestTarget)
if err != nil {
return err
}
if respMsg != nil {
cancelDnsReqCtx()
break
}
} }
var msg dnsmessage.Msg
if err = msg.Unpack(respBuf[:n]); err != nil {
return err
}
respMsg = &msg
cancelDnsReqCtx()
case consts.L4ProtoStr_TCP: case consts.L4ProtoStr_TCP:
// We can block here because we are in a coroutine. // We can block here because we are in a coroutine.
@ -677,7 +636,7 @@ func (c *DnsController) dialSend(invokingDepth int, req *udpRequest, data []byte
} }
}() }()
_ = conn.SetDeadline(time.Now().Add(5 * time.Second)) _ = conn.SetDeadline(time.Now().Add(4900 * time.Millisecond))
// We should write two byte length in the front of TCP DNS request. // We should write two byte length in the front of TCP DNS request.
bReq := pool.Get(2 + len(data)) bReq := pool.Get(2 + len(data))
defer pool.Put(bReq) defer pool.Put(bReq)
@ -705,13 +664,11 @@ func (c *DnsController) dialSend(invokingDepth int, req *udpRequest, data []byte
if n, err = io.ReadFull(conn, buf[:respLen]); err != nil { if n, err = io.ReadFull(conn, buf[:respLen]); err != nil {
return fmt.Errorf("failed to read DNS resp payload: %w", err) return fmt.Errorf("failed to read DNS resp payload: %w", err)
} }
respMsg, err = dnsRespHandler(buf[:n], dialArgument.bestTarget) var msg dnsmessage.Msg
if respMsg == nil && err == nil { if err = msg.Unpack(buf[:n]); err != nil {
err = fmt.Errorf("bad DNS response") return err
}
if err != nil {
return fmt.Errorf("failed to write DNS resp to client: %w", err)
} }
respMsg = &msg
default: default:
return fmt.Errorf("unexpected l4proto: %v", dialArgument.l4proto) return fmt.Errorf("unexpected l4proto: %v", dialArgument.l4proto)
} }
@ -743,6 +700,7 @@ func (c *DnsController) dialSend(invokingDepth int, req *udpRequest, data []byte
"upstream": upstreamName, "upstream": upstreamName,
}).Traceln("Reject with empty answer") }).Traceln("Reject with empty answer")
} }
// We also cache response reject.
default: default:
if c.log.IsLevelEnabled(logrus.TraceLevel) { if c.log.IsLevelEnabled(logrus.TraceLevel) {
c.log.WithFields(logrus.Fields{ c.log.WithFields(logrus.Fields{
@ -783,13 +741,16 @@ func (c *DnsController) dialSend(invokingDepth int, req *udpRequest, data []byte
return fmt.Errorf("unknown upstream: %v", upstreamIndex.String()) return fmt.Errorf("unknown upstream: %v", upstreamIndex.String())
} }
} }
// Keep the id the same with request. if err = c.NormalizeAndCacheDnsResp_(respMsg); err != nil {
respMsg.Id = id
data, err = respMsg.Pack()
if err != nil {
return err return err
} }
if needResp { if needResp {
// Keep the id the same with request.
respMsg.Id = id
data, err = respMsg.Pack()
if err != nil {
return err
}
if err = sendPkt(data, req.realDst, req.realSrc, req.src, req.lConn, req.lanWanFlag); err != nil { if err = sendPkt(data, req.realDst, req.realSrc, req.src, req.lConn, req.lanWanFlag); err != nil {
return err return err
} }