mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-10-04 14:56:49 +00:00
https://github.com/XTLS/Xray-core/pull/6718#issuecomment-5894987590 Fixes https://github.com/XTLS/Xray-core/issues/6692
386 lines
7.8 KiB
Go
386 lines
7.8 KiB
Go
package xdns
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/xtls/xray-core/common"
|
|
"github.com/xtls/xray-core/common/errors"
|
|
"github.com/xtls/xray-core/common/net"
|
|
"golang.org/x/net/dns/dnsmessage"
|
|
)
|
|
|
|
const (
|
|
maxResponseDelay = time.Second
|
|
)
|
|
|
|
type resp struct {
|
|
msg dnsmessage.Message
|
|
addr net.Addr
|
|
}
|
|
|
|
type Rec struct {
|
|
resp *Resp
|
|
clientID ClientID
|
|
addr net.Addr
|
|
}
|
|
|
|
type xdnsServer struct {
|
|
net.PacketConn
|
|
|
|
domains []*Domain
|
|
fragManager *FragManager
|
|
sendManager *SendManager
|
|
|
|
readCh chan packet
|
|
recCh chan *Rec
|
|
drCh chan resp
|
|
closeCh chan struct{}
|
|
wg sync.WaitGroup
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
func NewServer(c *Config, raw net.PacketConn) (net.PacketConn, error) {
|
|
if len(c.Domains) == 0 {
|
|
return nil, errors.New("empty domains")
|
|
}
|
|
domains := make([]*Domain, 0, len(c.Domains))
|
|
for i := range c.Domains {
|
|
types := make([]uint16, 0, len(c.Domains[i].Types))
|
|
for j := range c.Domains[i].Types {
|
|
types = append(types, uint16(c.Domains[i].Types[j]))
|
|
}
|
|
domain, err := NewDomain(c.Domains[i].Name, int(c.Domains[i].LenLimit), int(c.Domains[i].LabelLimit), types, uint16(c.Domains[i].Edns0))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
domains = append(domains, domain)
|
|
}
|
|
server := &xdnsServer{
|
|
PacketConn: raw,
|
|
|
|
domains: domains,
|
|
fragManager: NewFragManager(),
|
|
sendManager: NewSendManager(),
|
|
|
|
readCh: make(chan packet),
|
|
recCh: make(chan *Rec, 255),
|
|
drCh: make(chan resp),
|
|
closeCh: make(chan struct{}),
|
|
}
|
|
go server.run()
|
|
return server, nil
|
|
}
|
|
|
|
func (c *xdnsServer) closed() bool {
|
|
select {
|
|
case <-c.closeCh:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (c *xdnsServer) decref(msg dnsmessage.Message, addr net.Addr) {
|
|
select {
|
|
case c.drCh <- resp{msg: msg, addr: addr}:
|
|
default:
|
|
}
|
|
}
|
|
|
|
func (c *xdnsServer) read(buf []byte, addr net.Addr) {
|
|
msg := dnsmessage.Message{}
|
|
if err := msg.Unpack(buf); err != nil {
|
|
return
|
|
}
|
|
if msg.Header.Response {
|
|
return
|
|
}
|
|
|
|
if msg.Header.OpCode != 0 {
|
|
msg.Header.Response = true
|
|
msg.Header.RCode = dnsmessage.RCodeNotImplemented
|
|
c.decref(msg, addr)
|
|
return
|
|
}
|
|
|
|
if len(msg.Questions) != 1 {
|
|
msg.Header.Response = true
|
|
msg.Header.RCode = dnsmessage.RCodeFormatError
|
|
c.decref(msg, addr)
|
|
return
|
|
}
|
|
|
|
opt := false
|
|
edns0 := uint16(0)
|
|
for i := range msg.Additionals {
|
|
if msg.Additionals[i].Header.Type == dnsmessage.TypeOPT {
|
|
if opt {
|
|
msg.Header.RCode = dnsmessage.RCodeFormatError
|
|
c.decref(msg, addr)
|
|
return
|
|
}
|
|
opt = true
|
|
edns0 = uint16(msg.Additionals[i].Header.Class)
|
|
if ver := (msg.Additionals[i].Header.TTL >> 16) & 0xFF; ver != 0 {
|
|
msg.Header.RCode = dnsmessage.RCodeSuccess
|
|
msg.Additionals[i].Header.TTL = 1 << 24
|
|
c.decref(msg, addr)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
if opt {
|
|
if edns0 < 512 {
|
|
edns0 = 512
|
|
}
|
|
if edns0 > 4096 {
|
|
edns0 = 4096
|
|
}
|
|
}
|
|
errors.LogDebug(context.Background(), addr, " edns0 ", edns0, " buf ", len(buf), " ", msg.Questions[0].Type)
|
|
|
|
var domain *Domain
|
|
for i := range c.domains {
|
|
if c.domains[i].IsDomain(msg.Questions[0].Name) {
|
|
domain = c.domains[i]
|
|
break
|
|
}
|
|
}
|
|
if domain == nil {
|
|
msg.Header.Response = true
|
|
msg.Header.RCode = dnsmessage.RCodeNameError
|
|
c.decref(msg, addr)
|
|
return
|
|
}
|
|
if !domain.HasType(uint16(msg.Questions[0].Type)) {
|
|
msg.Header.Response = true
|
|
msg.Header.Authoritative = true
|
|
msg.Header.RCode = dnsmessage.RCodeSuccess
|
|
c.decref(msg, addr)
|
|
return
|
|
}
|
|
|
|
var decoded [255]byte
|
|
n := domain.Decode(&decoded, msg.Questions[0].Name)
|
|
if n < 9 {
|
|
msg.Header.Response = true
|
|
msg.Header.Authoritative = true
|
|
msg.Header.RCode = dnsmessage.RCodeSuccess
|
|
c.decref(msg, addr)
|
|
return
|
|
}
|
|
if TypeMap_[decoded[0]&3] != uint16(msg.Questions[0].Type) || (decoded[8]&0x3F != 3 && decoded[8]&0x3F != 8) || (decoded[8]&0x3F == 3 && n < 9+3+1) || (decoded[8]&0x3F == 8 && n != 9+8) {
|
|
msg.Header.Response = true
|
|
msg.Header.Authoritative = true
|
|
msg.Header.RCode = dnsmessage.RCodeSuccess
|
|
c.decref(msg, addr)
|
|
return
|
|
}
|
|
clientID := ClientIDFromRaw([8]byte(decoded[:8]))
|
|
|
|
r := NewResp(msg, domain, edns0)
|
|
if r == nil {
|
|
msg.Header.Response = true
|
|
msg.Header.Authoritative = true
|
|
msg.Header.RCode = dnsmessage.RCodeSuccess
|
|
c.decref(msg, addr)
|
|
return
|
|
}
|
|
select {
|
|
case c.recCh <- &Rec{resp: r, clientID: clientID, addr: addr}:
|
|
default:
|
|
msg.Header.Response = true
|
|
msg.Header.Authoritative = true
|
|
msg.Header.RCode = dnsmessage.RCodeSuccess
|
|
c.decref(msg, addr)
|
|
}
|
|
|
|
if decoded[8]&0x3F == 8 {
|
|
return
|
|
}
|
|
p := pool4K.Get().([]byte)
|
|
p = p[:0]
|
|
if decoded[8]&0xC0 == 0xC0 {
|
|
out := pool4K.Get().([]byte)
|
|
n := c.fragManager.Feed(out, FragKey{clientID: clientID, fragID: decoded[12]}, decoded[13], decoded[14], decoded[15:n])
|
|
pool4K.Put(p[:cap(p)])
|
|
if n > 0 {
|
|
p = out[:n]
|
|
} else {
|
|
pool4K.Put(out[:cap(out)])
|
|
return
|
|
}
|
|
} else {
|
|
p = append(p, decoded[12:n]...)
|
|
}
|
|
select {
|
|
case <-c.closeCh:
|
|
pool4K.Put(p[:cap(p)])
|
|
return
|
|
case c.readCh <- packet{p: p, addr: clientID.Addr()}:
|
|
return
|
|
}
|
|
}
|
|
|
|
func (c *xdnsServer) run() {
|
|
c.wg.Add(1)
|
|
go c.recv()
|
|
|
|
c.wg.Add(1)
|
|
go c.send()
|
|
|
|
c.wg.Add(1)
|
|
go c.dr()
|
|
|
|
c.wg.Wait()
|
|
close(c.readCh)
|
|
close(c.recCh)
|
|
close(c.drCh)
|
|
c.fragManager.Close()
|
|
c.sendManager.Close()
|
|
}
|
|
|
|
func (c *xdnsServer) recv() {
|
|
defer c.wg.Done()
|
|
|
|
var buf [512]byte
|
|
for {
|
|
n, addr, err := c.PacketConn.ReadFrom(buf[:])
|
|
if err != nil {
|
|
if c.closed() {
|
|
return
|
|
}
|
|
errors.LogErrorInner(context.Background(), err, "recv err")
|
|
return
|
|
}
|
|
c.read(buf[:n], addr)
|
|
}
|
|
}
|
|
|
|
func (c *xdnsServer) send() {
|
|
defer c.wg.Done()
|
|
|
|
timer := time.NewTimer(maxResponseDelay)
|
|
timer.Stop()
|
|
var buf [4096]byte
|
|
var data [4096]byte
|
|
var nextRec *Rec
|
|
for {
|
|
rec := nextRec
|
|
nextRec = nil
|
|
|
|
if rec == nil {
|
|
select {
|
|
case rec = <-c.recCh:
|
|
case <-c.closeCh:
|
|
return
|
|
}
|
|
}
|
|
|
|
ch, stash := c.sendManager.Pop(rec.clientID)
|
|
left := rec.resp.cap
|
|
timer.Reset(maxResponseDelay)
|
|
var ps [][]byte
|
|
for {
|
|
var p []byte
|
|
select {
|
|
case p = <-stash:
|
|
default:
|
|
select {
|
|
case p = <-stash:
|
|
case p = <-ch:
|
|
default:
|
|
select {
|
|
case p = <-stash:
|
|
case p = <-ch:
|
|
case <-timer.C:
|
|
case nextRec = <-c.recCh:
|
|
}
|
|
}
|
|
}
|
|
if len(p) == 0 {
|
|
break
|
|
}
|
|
timer.Reset(0)
|
|
left -= 2 + len(p)
|
|
if left < 0 {
|
|
if len(ps) == 0 {
|
|
errors.LogError(context.Background(), "err size ", len(p))
|
|
break
|
|
}
|
|
c.sendManager.Stash(rec.clientID, p)
|
|
break
|
|
}
|
|
ps = append(ps, p)
|
|
}
|
|
timer.Stop()
|
|
|
|
d := data[:0]
|
|
for i := range ps {
|
|
l := len(ps[i])
|
|
if i == len(ps)-1 {
|
|
l |= 0xC000
|
|
}
|
|
d = append(d, []byte{byte(l >> 8), byte(l)}...)
|
|
d = append(d, ps[i]...)
|
|
}
|
|
_, _ = c.PacketConn.WriteTo(rec.resp.Encode(buf[:0], d), rec.addr)
|
|
}
|
|
}
|
|
|
|
func (c *xdnsServer) dr() {
|
|
defer c.wg.Done()
|
|
|
|
var buf [512]byte
|
|
for {
|
|
select {
|
|
case <-c.closeCh:
|
|
return
|
|
case r := <-c.drCh:
|
|
_, _ = c.PacketConn.WriteTo(common.Must2(r.msg.AppendPack(buf[:0])), r.addr)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *xdnsServer) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
|
packet, ok := <-c.readCh
|
|
if ok {
|
|
n = copy(p, packet.p)
|
|
pool4K.Put(packet.p[:cap(packet.p)])
|
|
return n, packet.addr, nil
|
|
}
|
|
return 0, nil, io.ErrClosedPipe
|
|
}
|
|
|
|
func (c *xdnsServer) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
|
if c.closed() {
|
|
return 0, io.ErrClosedPipe
|
|
}
|
|
if len(p) == 0 || len(p) > 4096 {
|
|
errors.LogError(context.Background(), "err size ", len(p))
|
|
return 0, errors.New("err size")
|
|
}
|
|
c.sendManager.Push(ClientIDFromAddr(addr.(*net.UDPAddr)), p)
|
|
return len(p), nil
|
|
}
|
|
|
|
func (c *xdnsServer) Close() error {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
if c.closed() {
|
|
return nil
|
|
}
|
|
close(c.closeCh)
|
|
_ = c.PacketConn.Close()
|
|
return nil
|
|
}
|
|
|
|
func (c *xdnsServer) SetDeadline(t time.Time) error { return errors.New("not support") }
|
|
|
|
func (c *xdnsServer) SetReadDeadline(t time.Time) error { return errors.New("not support") }
|
|
|
|
func (c *xdnsServer) SetWriteDeadline(t time.Time) error { return errors.New("not support") }
|