feat: support reject in dns request routing

This commit is contained in:
mzz2017
2023-03-31 15:46:53 +08:00
parent f2dc750dbb
commit 6657fb329c
6 changed files with 65 additions and 38 deletions

View File

@ -9,7 +9,6 @@ import (
"github.com/mohae/deepcopy"
"golang.org/x/net/dns/dnsmessage"
"net/netip"
"strings"
"time"
)
@ -21,15 +20,16 @@ type DnsCache struct {
func (c *DnsCache) FillInto(req *dnsmessage.Message) {
req.Answers = deepcopy.Copy(c.Answers).([]dnsmessage.Resource)
// Align question and answer Name.
if len(req.Questions) > 0 {
q := req.Questions[0]
for i := range req.Answers {
if strings.EqualFold(req.Answers[i].Header.Name.String(), q.Name.String()) {
req.Answers[i].Header.Name.Data = q.Name.Data
}
}
}
// No need to align because of no flipping now.
//// Align question and answer Name.
//if len(req.Questions) > 0 {
// q := req.Questions[0]
// for i := range req.Answers {
// if strings.EqualFold(req.Answers[i].Header.Name.String(), q.Name.String()) {
// req.Answers[i].Header.Name.Data = q.Name.Data
// }
// }
//}
req.RCode = dnsmessage.RCodeSuccess
req.Response = true
req.RecursionAvailable = true

View File

@ -298,6 +298,41 @@ func (c *DnsController) Handle_(dnsMessage *dnsmessage.Message, req *udpRequest)
RefineSourceToShow(req.realSrc, req.realDst.Addr(), req.lanWanFlag), req.realDst.String(), strings.ToLower(q.Name.String()), q.Type,
)
}
//// NOTICE: Rush-answer detector was removed because it does not always work in all districts.
//// Make sure there is additional record OPT in the request to filter DNS rush-answer in the response process.
//// Because rush-answer has no resp OPT. We can distinguish them from multiple responses.
//// Note that additional record OPT may not be supported by home router either.
//_, _ = EnsureAdditionalOpt(dnsMessage, true)
// Route request.
upstreamIndex, upstream, err := c.routing.RequestSelect(dnsMessage)
if err != nil {
return err
}
if upstreamIndex == consts.DnsRequestOutboundIndex_Reject {
// Reject with empty answer.
dnsMessage.Answers = nil
dnsMessage.RCode = dnsmessage.RCodeSuccess
dnsMessage.Response = true
dnsMessage.RecursionAvailable = true
dnsMessage.Truncated = false
if c.log.IsLevelEnabled(logrus.TraceLevel) {
c.log.WithFields(logrus.Fields{
"question": dnsMessage.Questions,
}).Traceln("Reject with empty answer")
}
data, err := dnsMessage.Pack()
if err != nil {
return fmt.Errorf("pack DNS packet: %w", err)
}
if err = sendPkt(data, req.realDst, req.realSrc, req.src, req.lConn, req.lanWanFlag); err != nil {
return err
}
return nil
}
if resp := c.LookupDnsRespCache_(dnsMessage); resp != nil {
// Send cache to client directly.
if err = sendPkt(resp, req.realDst, req.realSrc, req.src, req.lConn, req.lanWanFlag); err != nil {
@ -312,20 +347,8 @@ func (c *DnsController) Handle_(dnsMessage *dnsmessage.Message, req *udpRequest)
return nil
}
//// NOTICE: Rush-answer detector was removed because it does not always work in all districts.
//// Make sure there is additional record OPT in the request to filter DNS rush-answer in the response process.
//// Because rush-answer has no resp OPT. We can distinguish them from multiple responses.
//// Note that additional record OPT may not be supported by home router either.
//_, _ = EnsureAdditionalOpt(dnsMessage, true)
// Route request.
upstream, err := c.routing.RequestSelect(dnsMessage)
if err != nil {
return err
}
if c.log.IsLevelEnabled(logrus.TraceLevel) {
upstreamName := "asis"
upstreamName := upstreamIndex.String()
if upstream != nil {
upstreamName = upstream.String()
}
@ -402,9 +425,6 @@ func (c *DnsController) dialSend(req *udpRequest, data []byte, id uint16, upstre
// We should set a connClosed flag to avoid it.
var connClosed bool
var conn netproxy.Conn
// TODO: Rewritten domain should not use full-cone (such as VMess Packet Addr).
// Maybe we should set up a mapping for UDP: Dialer + Target Domain => Remote Resolved IP.
// However, games may not use QUIC for communication, thus we cannot use domain to dial, which is fine.
switch dialArgument.l4proto {
case consts.L4ProtoStr_UDP:
// Get udp endpoint.