Files
Xray-core/app/dns/nameserver_udp.go
T

192 lines
5.3 KiB
Go
Raw Normal View History

2020-11-25 19:01:53 +08:00
package dns
import (
"context"
"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"
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-11-21 13:38:06 +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-11-21 13:38:06 +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)
2025-11-21 13:45:42 +08:00
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
}
2025-11-21 13:45:42 +08:00
// IsDisableCache implements Server.
func (s *ClassicNameServer) IsDisableCache() bool {
return s.cacheController.disableCache
}
// 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 {
2025-11-21 13:45:42 +08:00
errors.LogErrorInner(ctx, err, 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 {
2025-11-21 13:45:42 +08:00
errors.LogErrorInner(ctx, err, 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.updateRecord(&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
}
// getCacheController implements CachedNameserver.
func (s *ClassicNameServer) getCacheController() *CacheController {
return s.cacheController
}
// sendQuery implements CachedNameserver.
func (s *ClassicNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) {
2025-11-21 13:45:42 +08:00
errors.LogInfo(ctx, s.Name(), " querying DNS for: ", fqdn)
2020-11-25 19:01:53 +08:00
reqs := buildReqMsgs(fqdn, 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)
b, err := dns.PackMessage(req.msg)
if err != nil {
errors.LogErrorInner(ctx, err, "failed to pack dns query")
if noResponseErrCh != nil {
noResponseErrCh <- err
}
return
}
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) {
return queryIP(ctx, s, domain, option)
2020-11-25 19:01:53 +08:00
}