mirror of
https://github.com/daeuniverse/dae.git
synced 2025-01-05 21:11:55 +07:00
feat: use dns question flipping trick to reduce dns pollution
This commit is contained in:
parent
e42730ed81
commit
53872a24a5
@ -6,11 +6,14 @@
|
||||
package control
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"github.com/cilium/ebpf"
|
||||
"github.com/v2rayA/dae/common"
|
||||
"github.com/v2rayA/dae/common/consts"
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
"hash/fnv"
|
||||
"math/rand"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"time"
|
||||
@ -96,13 +99,39 @@ func (c *ControlPlane) LookupDnsRespCache(msg *dnsmessage.Message) (resp []byte)
|
||||
return nil
|
||||
}
|
||||
|
||||
// FlipDnsQuestionCase is used to reduce dns pollution.
|
||||
func FlipDnsQuestionCase(dm *dnsmessage.Message) {
|
||||
if len(dm.Questions) == 0 {
|
||||
return
|
||||
}
|
||||
q := &dm.Questions[0]
|
||||
// For reproducibility, we use dm.ID as input and add some entropy to make the results more discrete.
|
||||
h := fnv.New64()
|
||||
var buf [4]byte
|
||||
binary.BigEndian.PutUint16(buf[:], dm.ID)
|
||||
h.Write(buf[:2])
|
||||
binary.BigEndian.PutUint32(buf[:], 20230204) // entropy
|
||||
h.Write(buf[:])
|
||||
r := rand.New(rand.NewSource(int64(h.Sum64())))
|
||||
perm := r.Perm(int(q.Name.Length))
|
||||
for i := 0; i < int(q.Name.Length/3); i++ {
|
||||
j := perm[i]
|
||||
// Upper to lower; lower to upper.
|
||||
if q.Name.Data[j] >= 'a' && q.Name.Data[j] <= 'z' {
|
||||
q.Name.Data[j] -= 'a' - 'A'
|
||||
} else if q.Name.Data[j] >= 'A' && q.Name.Data[j] <= 'Z' {
|
||||
q.Name.Data[j] += 'a' - 'A'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DnsRespHandler handle DNS resp. This function should be invoked when cache miss.
|
||||
func (c *ControlPlane) DnsRespHandler(data []byte) (newData []byte, err error) {
|
||||
var msg dnsmessage.Message
|
||||
if err = msg.Unpack(data); err != nil {
|
||||
return nil, fmt.Errorf("unpack dns pkt: %w", err)
|
||||
}
|
||||
|
||||
FlipDnsQuestionCase(&msg)
|
||||
// Check healthy.
|
||||
if !msg.Response || msg.RCode != dnsmessage.RCodeSuccess || len(msg.Questions) == 0 {
|
||||
return data, nil
|
||||
@ -140,8 +169,8 @@ func (c *ControlPlane) DnsRespHandler(data []byte) (newData []byte, err error) {
|
||||
|
||||
// Update dnsCache.
|
||||
c.mutex.Lock()
|
||||
fqdn := q.Name.String()
|
||||
cacheKey := strings.ToLower(fqdn) + q.Type.String()
|
||||
fqdn := strings.ToLower(q.Name.String())
|
||||
cacheKey := fqdn + q.Type.String()
|
||||
cache, ok := c.dnsCache[cacheKey]
|
||||
if ok {
|
||||
c.mutex.Unlock()
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"golang.org/x/net/dns/dnsmessage"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -122,7 +123,7 @@ func (c *ControlPlane) handlePkt(data []byte, lConn *net.UDPConn, lAddrPort neti
|
||||
if c.log.IsLevelEnabled(logrus.DebugLevel) && len(dnsMessage.Questions) > 0 {
|
||||
q := dnsMessage.Questions[0]
|
||||
c.log.Tracef("UDP(DNS) %v <-[%v]-> Cache: %v %v",
|
||||
RefineSourceToShow(lAddrPort, dest.Addr()), outbound.Name, q.Name, q.Type,
|
||||
RefineSourceToShow(lAddrPort, dest.Addr()), outbound.Name, strings.ToLower(q.Name.String()), q.Type,
|
||||
)
|
||||
}
|
||||
return nil
|
||||
@ -132,6 +133,12 @@ func (c *ControlPlane) handlePkt(data []byte, lConn *net.UDPConn, lAddrPort neti
|
||||
// NOTICE: Routing was calculated in advance by the eBPF program.
|
||||
dummyFrom = &addrHdr.Dest
|
||||
dest = c.dnsUpstream
|
||||
|
||||
// Flip dns question to reduce dns pollution.
|
||||
FlipDnsQuestionCase(dnsMessage)
|
||||
if data, err = dnsMessage.Pack(); err != nil {
|
||||
return fmt.Errorf("pack flipped dns packet: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,7 +166,7 @@ func (c *ControlPlane) handlePkt(data []byte, lConn *net.UDPConn, lAddrPort neti
|
||||
"l4proto": "UDP(DNS)",
|
||||
"outbound": outbound.Name,
|
||||
"dialer": d.Name(),
|
||||
"qname": q.Name,
|
||||
"qname": strings.ToLower(q.Name.String()),
|
||||
"qtype": q.Type,
|
||||
}).Infof("%v <-> %v",
|
||||
RefineSourceToShow(lAddrPort, dest.Addr()), RefineAddrPortToShow(dest),
|
||||
|
@ -9,7 +9,7 @@ global {
|
||||
# Now only support UDP and IP:Port.
|
||||
# Please make sure DNS traffic will go through and be forwarded by dae.
|
||||
# The request to dns upstream follows routing defined below.
|
||||
dns_upstream: '1.1.1.1:53'
|
||||
dns_upstream: '8.8.8.8:53'
|
||||
|
||||
# The LAN interface to bind. Use it if you only want to proxy LAN instead of localhost.
|
||||
# Multiple interfaces split by ",".
|
||||
@ -62,7 +62,7 @@ group {
|
||||
routing {
|
||||
# See routing.md for full examples.
|
||||
|
||||
ip(1.1.1.1) && port(53) -> my_group
|
||||
pname(dae) && ip(8.8.8.8) && port(53) -> direct
|
||||
|
||||
pname(firefox) && domain(ip.sb) -> direct
|
||||
pname(curl) && domain(ip.sb) -> my_group
|
||||
|
Loading…
Reference in New Issue
Block a user