XDNS finalmask: Refine upload (#7095)

Fixes https://github.com/XTLS/Xray-core/pull/7090#issuecomment-6009651970
This commit is contained in:
LjhAUMEM authored and GitHub committed 2026-10-08 04:58:49 +00:00
1 parent 9e55a6ed18
commit 37184948d4
2 files changed
+135 -165

No files matched your search

+121 -141
View File
@@ -47,7 +47,6 @@ type xdnsClient struct {
resolverIndex atomic.Uint32 resolverIndex atomic.Uint32
readCh chan packet readCh chan packet
sendCh chan []byte
poolCh chan struct{} poolCh chan struct{}
closeCh chan struct{} closeCh chan struct{}
wg sync.WaitGroup wg sync.WaitGroup
@@ -101,7 +100,6 @@ func NewClient(c *Config, dialer *finalmask.Dialer) (net.PacketConn, error) {
resolverSends: make([]atomic.Uint32, len(c.Resolvers)), resolverSends: make([]atomic.Uint32, len(c.Resolvers)),
readCh: make(chan packet), readCh: make(chan packet),
sendCh: make(chan []byte, 16),
poolCh: make(chan struct{}, pollLimit), poolCh: make(chan struct{}, pollLimit),
closeCh: make(chan struct{}), closeCh: make(chan struct{}),
} }
@@ -118,6 +116,107 @@ func (c *xdnsClient) closed() bool {
} }
} }
func (c *xdnsClient) send(p []byte) {
domain := c.domains[mrand.Intn(len(c.domains))]
qtype := domain.types[mrand.Intn(len(domain.types))]
var buf [512]byte
var data [255]byte
send := func(p []byte) {
msg := dnsmessage.Message{
Header: dnsmessage.Header{
RecursionDesired: true,
},
Questions: []dnsmessage.Question{
{
Name: domain.Encode(p),
Type: dnsmessage.Type(qtype),
Class: dnsmessage.ClassINET,
},
},
}
if domain.edns0 > 0 {
msg.Additionals = []dnsmessage.Resource{
{
Header: dnsmessage.ResourceHeader{
Name: dnsmessage.MustNewName("."),
Type: dnsmessage.TypeOPT,
Class: dnsmessage.Class(domain.edns0),
TTL: 0,
},
Body: &dnsmessage.OPTResource{},
},
}
}
pack := common.Must2(msg.AppendPack(buf[:0]))
common.Must2(rand.Read(pack[:2]))
index := c.resolverIndex.Load()
cur := c.resolverSends[index].Add(1)
i := index
for {
i++
if i == uint32(len(c.resolvers)) {
i = 0
}
if i == index {
break
}
if cur > c.resolverSends[i].Load() {
break
}
}
c.resolverIndex.Store(i)
c.resolvers[index].Send(pack)
}
if len(p) == 0 {
copy(data[:], c.clientID[:])
data[0] |= TypeMap[qtype]
data[8] = 8
common.Must2(rand.Read(data[9:17]))
send(data[:17])
return
}
if len(p) <= domain.cap-12 {
copy(data[:], c.clientID[:])
data[0] |= TypeMap[qtype]
data[8] = 3
common.Must2(rand.Read(data[9:12]))
copy(data[12:], p)
send(data[:12+len(p)])
return
}
if len(p) <= 255*(domain.cap-15) {
copy(data[:], c.clientID[:])
data[0] |= TypeMap[qtype]
data[8] = 3 | 0xC0
common.Must2(rand.Read(data[9:12]))
fragID := byte(c.fragID.Add(1))
fragN := len(p) / (domain.cap - 15)
if len(p)%(domain.cap-15) > 0 {
fragN++
}
for i := range fragN {
data[12] = fragID
data[13] = byte(i)
data[14] = byte(fragN)
size := min(len(p), domain.cap-15)
copy(data[15:], p[:size])
send(data[:15+size])
p = p[size:]
}
return
}
errors.LogError(context.Background(), "err size ", len(p))
}
func (c *xdnsClient) read(buf []byte, addr net.Addr) bool { func (c *xdnsClient) read(buf []byte, addr net.Addr) bool {
msg := dnsmessage.Message{} msg := dnsmessage.Message{}
if err := msg.Unpack(buf); err != nil { if err := msg.Unpack(buf); err != nil {
@@ -193,11 +292,10 @@ func (c *xdnsClient) run() {
} }
c.wg.Add(1) c.wg.Add(1)
go c.send() go c.poll()
c.wg.Wait() c.wg.Wait()
close(c.readCh) close(c.readCh)
close(c.sendCh)
close(c.poolCh) close(c.poolCh)
} }
@@ -224,152 +322,36 @@ func (c *xdnsClient) recv(i int) {
} }
} }
func (c *xdnsClient) send() { func (c *xdnsClient) poll() {
defer c.wg.Done() defer c.wg.Done()
var buf [512]byte select {
var data [255]byte case <-c.closeCh:
case <-c.poolCh:
sendMsg := func(p []byte, domain *Domain, qtype uint16) {
msg := dnsmessage.Message{
Header: dnsmessage.Header{
RecursionDesired: true,
},
Questions: []dnsmessage.Question{
{
Name: domain.Encode(p),
Type: dnsmessage.Type(qtype),
Class: dnsmessage.ClassINET,
},
},
}
if domain.edns0 > 0 {
msg.Additionals = []dnsmessage.Resource{
{
Header: dnsmessage.ResourceHeader{
Name: dnsmessage.MustNewName("."),
Type: dnsmessage.TypeOPT,
Class: dnsmessage.Class(domain.edns0),
TTL: 0,
},
Body: &dnsmessage.OPTResource{},
},
}
}
pack := common.Must2(msg.AppendPack(buf[:0]))
common.Must2(rand.Read(pack[:2]))
index := c.resolverIndex.Load()
cur := c.resolverSends[index].Add(1)
i := index
for {
i++
if i == uint32(len(c.resolvers)) {
i = 0
}
if i == index {
break
}
if cur > c.resolverSends[i].Load() {
break
}
}
c.resolverIndex.Store(i)
c.resolvers[index].Send(pack)
} }
send := func(p []byte) {
domain := c.domains[mrand.Intn(len(c.domains))]
qtype := domain.types[mrand.Intn(len(domain.types))]
if len(p) == 0 {
copy(data[:], c.clientID[:])
data[0] |= TypeMap[qtype]
data[8] = 8
common.Must2(rand.Read(data[9:17]))
sendMsg(data[:17], domain, qtype)
return
}
if len(p) <= domain.cap-12 {
copy(data[:], c.clientID[:])
data[0] |= TypeMap[qtype]
data[8] = 3
common.Must2(rand.Read(data[9:12]))
copy(data[12:], p)
sendMsg(data[:12+len(p)], domain, qtype)
return
}
if len(p) <= 255*(domain.cap-15) {
copy(data[:], c.clientID[:])
data[0] |= TypeMap[qtype]
data[8] = 3 | 0xC0
common.Must2(rand.Read(data[9:12]))
fragID := byte(c.fragID.Add(1))
fragN := len(p) / (domain.cap - 15)
if len(p)%(domain.cap-15) > 0 {
fragN++
}
for i := range fragN {
data[12] = fragID
data[13] = byte(i)
data[14] = byte(fragN)
size := min(len(p), domain.cap-15)
copy(data[15:], p[:size])
sendMsg(data[:15+size], domain, qtype)
p = p[size:]
}
return
}
errors.LogError(context.Background(), "err size ", len(p))
}
ticker := time.NewTicker(initPollDelay)
defer ticker.Stop()
delay := initPollDelay delay := initPollDelay
p := []byte(nil) ticker := time.NewTicker(delay)
timeout := false defer ticker.Stop()
for { for {
select { select {
case <-c.closeCh: case <-c.closeCh:
return return
default: case <-c.poolCh:
select { delay = initPollDelay
case <-c.closeCh: case <-ticker.C:
return
case p = <-c.sendCh:
case <-c.poolCh:
case <-ticker.C:
timeout = true
}
}
if len(p) > 0 {
select {
case <-c.poolCh:
default:
}
}
send(p)
for range c.extraPoll {
send(nil)
}
if timeout {
delay *= pollDelayMultiplier delay *= pollDelayMultiplier
if delay > maxPollDelay { if delay > maxPollDelay {
delay = maxPollDelay delay = maxPollDelay
} }
timeout = false }
} else { if c.closed() {
delay = initPollDelay return
} }
ticker.Reset(delay) ticker.Reset(delay)
c.send(nil)
for range c.extraPoll {
c.send(nil)
}
} }
} }
@@ -391,11 +373,9 @@ func (c *xdnsClient) WriteTo(p []byte, addr net.Addr) (n int, err error) {
errors.LogError(context.Background(), "err size ", len(p)) errors.LogError(context.Background(), "err size ", len(p))
return 0, errors.New("err size") return 0, errors.New("err size")
} }
b := make([]byte, len(p)) c.send(p)
copy(b, p) for range c.extraPoll {
select { c.send(nil)
case c.sendCh <- b:
default:
} }
return len(p), nil return len(p), nil
} }
+14 -24
View File
@@ -6,10 +6,9 @@ import (
) )
const ( const (
fragTTL = 8 * time.Second fragTTL = 4 * time.Second
fragSize = 4096 fragSize = 4096
fragClientIDSize = 16384 fragCount = 4096
fragCount = 4096
) )
type FragKey struct { type FragKey struct {
@@ -26,17 +25,15 @@ type FragEntry struct {
} }
type FragManager struct { type FragManager struct {
m map[FragKey]*FragEntry m map[FragKey]*FragEntry
sizem map[ClientID]int ch chan struct{}
ch chan struct{} mu sync.Mutex
mu sync.Mutex
} }
func NewFragManager() *FragManager { func NewFragManager() *FragManager {
m := &FragManager{ m := &FragManager{
m: make(map[FragKey]*FragEntry), m: make(map[FragKey]*FragEntry),
sizem: make(map[ClientID]int), ch: make(chan struct{}),
ch: make(chan struct{}),
} }
go m.gc() go m.gc()
return m return m
@@ -51,9 +48,8 @@ func (m *FragManager) closed() bool {
} }
} }
func (m *FragManager) removeEntey(k FragKey, e *FragEntry) { func (m *FragManager) remove(key FragKey) {
m.sizem[k.clientID] -= e.size delete(m.m, key)
delete(m.m, k)
} }
func (m *FragManager) tryRemove() { func (m *FragManager) tryRemove() {
@@ -70,7 +66,7 @@ func (m *FragManager) tryRemove() {
first = false first = false
} }
} }
m.removeEntey(key, entry) m.remove(key)
} }
func (m *FragManager) gc() { func (m *FragManager) gc() {
@@ -84,7 +80,7 @@ func (m *FragManager) gc() {
m.mu.Lock() m.mu.Lock()
for k, e := range m.m { for k, e := range m.m {
if now.After(e.deadline) { if now.After(e.deadline) {
m.removeEntey(k, e) m.remove(k)
} }
} }
m.mu.Unlock() m.mu.Unlock()
@@ -109,7 +105,7 @@ func (m *FragManager) Feed(out []byte, key FragKey, fragIdx, fragN byte, data []
if entry == nil { if entry == nil {
m.tryRemove() m.tryRemove()
} else { } else {
m.removeEntey(key, entry) m.remove(key)
} }
entry = &FragEntry{ entry = &FragEntry{
data: make([][]byte, fragN), data: make([][]byte, fragN),
@@ -131,11 +127,6 @@ func (m *FragManager) Feed(out []byte, key FragKey, fragIdx, fragN byte, data []
if entry.size+len(data) > fragSize { if entry.size+len(data) > fragSize {
return 0 return 0
} }
if entry.len < int(entry.total)-1 {
if m.sizem[key.clientID]+len(data) > fragClientIDSize {
return 0
}
}
cp := make([]byte, len(data)) cp := make([]byte, len(data))
copy(cp, data) copy(cp, data)
@@ -144,7 +135,6 @@ func (m *FragManager) Feed(out []byte, key FragKey, fragIdx, fragN byte, data []
entry.size += len(data) entry.size += len(data)
entry.len++ entry.len++
entry.deadline = now.Add(fragTTL) entry.deadline = now.Add(fragTTL)
m.sizem[key.clientID] += len(data)
if entry.len < int(entry.total) { if entry.len < int(entry.total) {
return 0 return 0
@@ -154,7 +144,7 @@ func (m *FragManager) Feed(out []byte, key FragKey, fragIdx, fragN byte, data []
for i := range entry.data { for i := range entry.data {
out = append(out, entry.data[i]...) out = append(out, entry.data[i]...)
} }
m.removeEntey(key, entry) m.remove(key)
return len(out) return len(out)
} }