Files

242 lines
6.8 KiB
Go
Raw Permalink Normal View History

2020-11-25 19:01:53 +08:00
package dns
import (
"context"
go_errors "errors"
2020-11-25 19:01:53 +08:00
"strings"
"sync"
"sync/atomic"
"time"
2020-12-04 09:36:16 +08:00
"github.com/xtls/xray-core/common"
2024-06-29 14:32:57 -04:00
"github.com/xtls/xray-core/common/errors"
2021-03-07 00:29:17 +08:00
"github.com/xtls/xray-core/common/log"
2020-12-04 09:36:16 +08:00
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol/dns"
udp_proto "github.com/xtls/xray-core/common/protocol/udp"
"github.com/xtls/xray-core/common/task"
dns_feature "github.com/xtls/xray-core/features/dns"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/transport/internet/udp"
2022-05-18 15:29:01 +08:00
"golang.org/x/net/dns/dnsmessage"
2020-11-25 19:01:53 +08:00
)
2021-10-16 21:02:51 +08:00
// ClassicNameServer implemented traditional UDP DNS.
2020-11-25 19:01:53 +08:00
type ClassicNameServer struct {
sync.RWMutex
cacheController *CacheController
address *net.Destination
requests map[uint16]*udpDnsRequest
udpServer *udp.Dispatcher
requestsCleanup *task.Periodic
reqID uint32
clientIP net.IP
2020-11-25 19:01:53 +08:00
}
type udpDnsRequest struct {
dnsRequest
ctx context.Context
}
2021-10-16 21:02:51 +08:00
// NewClassicNameServer creates udp server object for remote resolving.
2025-10-19 02:24:04 +08:00
func NewClassicNameServer(address net.Destination, dispatcher routing.Dispatcher, disableCache bool, serveStale bool, serveExpiredTTL uint32, clientIP net.IP) *ClassicNameServer {
2020-11-25 19:01:53 +08:00
// default to 53 if unspecific
if address.Port == 0 {
address.Port = net.Port(53)
}
s := &ClassicNameServer{
2025-10-19 02:24:04 +08:00
cacheController: NewCacheController(strings.ToUpper(address.String()), disableCache, serveStale, serveExpiredTTL),
address: &address,
requests: make(map[uint16]*udpDnsRequest),
clientIP: clientIP,
2020-11-25 19:01:53 +08:00
}
s.requestsCleanup = &task.Periodic{
2020-11-25 19:01:53 +08:00
Interval: time.Minute,
Execute: s.RequestsCleanup,
2020-11-25 19:01:53 +08:00
}
s.udpServer = udp.NewDispatcher(dispatcher, s.HandleResponse)
2024-06-29 14:32:57 -04:00
errors.LogInfo(context.Background(), "DNS: created UDP client initialized for ", address.NetAddr())
2020-11-25 19:01:53 +08:00
return s
}
2021-10-16 21:02:51 +08:00
// Name implements Server.
2020-11-25 19:01:53 +08:00
func (s *ClassicNameServer) Name() string {
return s.cacheController.name
2020-11-25 19:01:53 +08:00
}
// RequestsCleanup clears expired items from cache
func (s *ClassicNameServer) RequestsCleanup() error {
2020-11-25 19:01:53 +08:00
now := time.Now()
s.Lock()
defer s.Unlock()
if len(s.requests) == 0 {
return errors.New(s.Name(), " nothing to do. stopping...")
2020-11-25 19:01:53 +08:00
}
for id, req := range s.requests {
if req.expire.Before(now) {
delete(s.requests, id)
}
}
if len(s.requests) == 0 {
s.requests = make(map[uint16]*udpDnsRequest)
2020-11-25 19:01:53 +08:00
}
return nil
}
2021-10-16 21:02:51 +08:00
// HandleResponse handles udp response packet from remote DNS server.
2020-11-25 19:01:53 +08:00
func (s *ClassicNameServer) HandleResponse(ctx context.Context, packet *udp_proto.Packet) {
payload := packet.Payload
ipRec, err := parseResponse(payload.Bytes())
payload.Release()
2020-11-25 19:01:53 +08:00
if err != nil {
errors.LogError(ctx, s.Name(), " fail to parse responded DNS udp")
2020-11-25 19:01:53 +08:00
return
}
s.Lock()
id := ipRec.ReqID
req, ok := s.requests[id]
if ok {
// remove the pending request
delete(s.requests, id)
}
s.Unlock()
if !ok {
errors.LogError(ctx, s.Name(), " cannot find the pending request")
2020-11-25 19:01:53 +08:00
return
}
// if truncated, retry with EDNS0 option(udp payload size: 1350)
if ipRec.RawHeader.Truncated {
// if already has EDNS0 option, no need to retry
if len(req.msg.Additionals) == 0 {
// copy necessary meta data from original request
// and add EDNS0 option
opt := new(dnsmessage.Resource)
common.Must(opt.Header.SetEDNS0(1350, 0xfe00, true))
opt.Body = &dnsmessage.OPTResource{}
newMsg := *req.msg
newReq := *req
newMsg.Additionals = append(newMsg.Additionals, *opt)
newMsg.ID = s.newReqID()
newReq.msg = &newMsg
s.addPendingRequest(&newReq)
b, _ := dns.PackMessage(newReq.msg)
copyDest := net.UDPDestination(s.address.Address, s.address.Port)
b.UDP = &copyDest
s.udpServer.Dispatch(toDnsContext(newReq.ctx, s.address.String()), *s.address, b)
return
}
}
s.cacheController.updateIP(&req.dnsRequest, ipRec)
2020-11-25 19:01:53 +08:00
}
func (s *ClassicNameServer) newReqID() uint16 {
return uint16(atomic.AddUint32(&s.reqID, 1))
}
func (s *ClassicNameServer) addPendingRequest(req *udpDnsRequest) {
2020-11-25 19:01:53 +08:00
s.Lock()
id := req.msg.ID
req.expire = time.Now().Add(time.Second * 8)
2021-10-16 21:02:51 +08:00
s.requests[id] = req
s.Unlock()
common.Must(s.requestsCleanup.Start())
2020-11-25 19:01:53 +08:00
}
func (s *ClassicNameServer) sendQuery(ctx context.Context, _ chan<- error, domain string, option dns_feature.IPOption) {
errors.LogDebug(ctx, s.Name(), " querying DNS for: ", domain)
2020-11-25 19:01:53 +08:00
reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
2020-11-25 19:01:53 +08:00
for _, req := range reqs {
udpReq := &udpDnsRequest{
dnsRequest: *req,
ctx: ctx,
}
s.addPendingRequest(udpReq)
2020-11-25 19:01:53 +08:00
b, _ := dns.PackMessage(req.msg)
copyDest := net.UDPDestination(s.address.Address, s.address.Port)
b.UDP = &copyDest
2022-07-24 23:44:16 -04:00
s.udpServer.Dispatch(toDnsContext(ctx, s.address.String()), *s.address, b)
2020-11-25 19:01:53 +08:00
}
}
2021-03-06 23:39:50 -05:00
// QueryIP implements Server.
func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, option dns_feature.IPOption) ([]net.IP, uint32, error) {
2020-11-25 19:01:53 +08:00
fqdn := Fqdn(domain)
sub4, sub6 := s.cacheController.registerSubscribers(fqdn, option)
defer closeSubscribers(sub4, sub6)
2020-11-25 19:01:53 +08:00
queryOption := option
if s.cacheController.disableCache {
errors.LogDebug(ctx, "DNS cache is disabled. Querying IP for ", domain, " at ", s.Name())
2021-10-16 21:02:51 +08:00
} else {
ips, ttl, isARecordExpired, isAAAARecordExpired, err := s.cacheController.findIPsForDomain(fqdn, option)
if sub4 != nil && !isARecordExpired {
sub4.Close()
sub4 = nil
queryOption.IPv4Enable = false
}
if sub6 != nil && !isAAAARecordExpired {
sub6.Close()
sub6 = nil
queryOption.IPv6Enable = false
}
if !go_errors.Is(err, errRecordNotFound) {
2025-10-19 02:24:04 +08:00
if ttl > 0 {
errors.LogDebugInner(ctx, err, s.Name(), " cache HIT ", domain, " -> ", ips)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSCacheHit, Elapsed: 0, Error: err})
return ips, uint32(ttl), err
}
if s.cacheController.serveStale && (s.cacheController.serveExpiredTTL == 0 || s.cacheController.serveExpiredTTL < ttl) {
errors.LogDebugInner(ctx, err, s.Name(), " cache OPTIMISTE ", domain, " -> ", ips)
s.sendQuery(ctx, nil, fqdn, queryOption)
2025-10-19 02:24:04 +08:00
return ips, 1, err
}
2021-10-16 21:02:51 +08:00
}
2020-11-25 19:01:53 +08:00
}
noResponseErrCh := make(chan error, 2)
s.sendQuery(ctx, noResponseErrCh, fqdn, queryOption)
2021-03-07 00:29:17 +08:00
start := time.Now()
2020-11-25 19:01:53 +08:00
if sub4 != nil {
select {
case <-ctx.Done():
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub4.Wait():
sub4.Close()
2020-11-25 19:01:53 +08:00
}
}
if sub6 != nil {
2020-11-25 19:01:53 +08:00
select {
case <-ctx.Done():
return nil, 0, ctx.Err()
case err := <-noResponseErrCh:
return nil, 0, err
case <-sub6.Wait():
sub6.Close()
2020-11-25 19:01:53 +08:00
}
}
ips, ttl, _, _, err := s.cacheController.findIPsForDomain(fqdn, option)
log.Record(&log.DNSLog{Server: s.Name(), Domain: domain, Result: ips, Status: log.DNSQueried, Elapsed: time.Since(start), Error: err})
2025-10-19 02:24:04 +08:00
var rTTL uint32
if ttl <= 0 {
rTTL = 1
} else {
rTTL = uint32(ttl)
}
return ips, rTTL, err
2020-11-25 19:01:53 +08:00
}