mirror of
https://github.com/daeuniverse/dae.git
synced 2024-12-22 20:14:40 +07:00
feat: support reject in dns request routing
This commit is contained in:
parent
f2dc750dbb
commit
6657fb329c
@ -10,9 +10,10 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
type DnsRequestOutboundIndex uint8
|
||||
type DnsRequestOutboundIndex int16
|
||||
|
||||
const (
|
||||
DnsRequestOutboundIndex_Reject DnsRequestOutboundIndex = 0xFC
|
||||
DnsRequestOutboundIndex_AsIs DnsRequestOutboundIndex = 0xFD
|
||||
DnsRequestOutboundIndex_LogicalOr DnsRequestOutboundIndex = 0xFE
|
||||
DnsRequestOutboundIndex_LogicalAnd DnsRequestOutboundIndex = 0xFF
|
||||
@ -23,6 +24,8 @@ const (
|
||||
|
||||
func (i DnsRequestOutboundIndex) String() string {
|
||||
switch i {
|
||||
case DnsRequestOutboundIndex_Reject:
|
||||
return "reject"
|
||||
case DnsRequestOutboundIndex_AsIs:
|
||||
return "asis"
|
||||
case DnsRequestOutboundIndex_LogicalOr:
|
||||
|
@ -142,9 +142,9 @@ func (s *Dns) InitUpstreams() {
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func (s *Dns) RequestSelect(msg *dnsmessage.Message) (upstream *Upstream, err error) {
|
||||
func (s *Dns) RequestSelect(msg *dnsmessage.Message) (upstreamIndex consts.DnsRequestOutboundIndex, upstream *Upstream, err error) {
|
||||
if msg.Response {
|
||||
return nil, fmt.Errorf("DNS request expected but DNS response received")
|
||||
return 0, nil, fmt.Errorf("DNS request expected but DNS response received")
|
||||
}
|
||||
|
||||
// Prepare routing.
|
||||
@ -159,23 +159,24 @@ func (s *Dns) RequestSelect(msg *dnsmessage.Message) (upstream *Upstream, err er
|
||||
qtype = q.Type
|
||||
}
|
||||
// Route.
|
||||
upstreamIndex, err := s.reqMatcher.Match(qname, qtype)
|
||||
upstreamIndex, err = s.reqMatcher.Match(qname, qtype)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return 0, nil, err
|
||||
}
|
||||
// nil indicates AsIs.
|
||||
if upstreamIndex == consts.DnsRequestOutboundIndex_AsIs {
|
||||
return nil, nil
|
||||
if upstreamIndex == consts.DnsRequestOutboundIndex_AsIs ||
|
||||
upstreamIndex == consts.DnsRequestOutboundIndex_Reject {
|
||||
return upstreamIndex, nil, nil
|
||||
}
|
||||
if int(upstreamIndex) >= len(s.upstream) {
|
||||
return nil, fmt.Errorf("bad upstream index: %v not in [0, %v]", upstreamIndex, len(s.upstream)-1)
|
||||
return 0, nil, fmt.Errorf("bad upstream index: %v not in [0, %v]", upstreamIndex, len(s.upstream)-1)
|
||||
}
|
||||
// Get corresponding upstream.
|
||||
upstream, err = s.upstream[upstreamIndex].GetUpstream()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return 0, nil, err
|
||||
}
|
||||
return upstream, nil
|
||||
return upstreamIndex, upstream, nil
|
||||
}
|
||||
|
||||
func (s *Dns) ResponseSelect(msg *dnsmessage.Message, fromUpstream *Upstream) (upstreamIndex consts.DnsResponseOutboundIndex, upstream *Upstream, err error) {
|
||||
|
@ -43,6 +43,8 @@ func NewRequestMatcherBuilder(log *logrus.Logger, rules []*config_parser.Routing
|
||||
|
||||
func (b *RequestMatcherBuilder) upstreamToId(upstream string) (upstreamId consts.DnsRequestOutboundIndex, err error) {
|
||||
switch upstream {
|
||||
case consts.DnsRequestOutboundIndex_Reject.String():
|
||||
upstreamId = consts.DnsRequestOutboundIndex_Reject
|
||||
case consts.DnsRequestOutboundIndex_AsIs.String():
|
||||
upstreamId = consts.DnsRequestOutboundIndex_AsIs
|
||||
case consts.DnsRequestOutboundIndex_LogicalAnd.String():
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -23,12 +23,13 @@ dns {
|
||||
# According to the request of dns query, decide to use which DNS upstream.
|
||||
# Match rules from top to bottom.
|
||||
request {
|
||||
# Built-in upstream in 'request': asis.
|
||||
# Built-in outbounds in 'request': asis, reject.
|
||||
# You can also use user-defined upstreams.
|
||||
|
||||
# Available functions: qname, qtype.
|
||||
|
||||
# DNS request name (omit suffix dot '.').
|
||||
qname(geosite:category-ads-all) -> reject
|
||||
qname(suffix: abc.com, keyword: google) -> googledns
|
||||
qname(full: ok.com, regex: '^yes') -> googledns
|
||||
# DNS request type
|
||||
@ -41,7 +42,7 @@ dns {
|
||||
# According to the response of dns query, decide to accept or re-lookup using another DNS upstream.
|
||||
# Match rules from top to bottom.
|
||||
response {
|
||||
# No built-in upstream in 'response'.
|
||||
# Built-in outbounds in 'response': accept, reject.
|
||||
# You can use user-defined upstreams.
|
||||
|
||||
# Available functions: qname, qtype, upstream, ip.
|
||||
|
Loading…
Reference in New Issue
Block a user