DNS: Avoid panic on domain too long (#6207)

Fixes https://github.com/XTLS/Xray-core/issues/6204
This commit is contained in:
Meow
2026-05-29 04:06:32 +08:00
committed by GitHub
parent 1cd7d25fec
commit a2cec2e580
6 changed files with 75 additions and 10 deletions
+9 -4
View File
@@ -127,15 +127,20 @@ func genEDNS0Options(clientIP net.IP, padding int) *dnsmessage.Resource {
return opt return opt
} }
func buildReqMsgs(domain string, option dns_feature.IPOption, reqIDGen func() uint16, reqOpts *dnsmessage.Resource) []*dnsRequest { func buildReqMsgs(domain string, option dns_feature.IPOption, reqIDGen func() uint16, reqOpts *dnsmessage.Resource) ([]*dnsRequest, error) {
name, err := dnsmessage.NewName(domain)
if err != nil {
return nil, err
}
qA := dnsmessage.Question{ qA := dnsmessage.Question{
Name: dnsmessage.MustNewName(domain), Name: name,
Type: dnsmessage.TypeA, Type: dnsmessage.TypeA,
Class: dnsmessage.ClassINET, Class: dnsmessage.ClassINET,
} }
qAAAA := dnsmessage.Question{ qAAAA := dnsmessage.Question{
Name: dnsmessage.MustNewName(domain), Name: name,
Type: dnsmessage.TypeAAAA, Type: dnsmessage.TypeAAAA,
Class: dnsmessage.ClassINET, Class: dnsmessage.ClassINET,
} }
@@ -175,7 +180,7 @@ func buildReqMsgs(domain string, option dns_feature.IPOption, reqIDGen func() ui
}) })
} }
return reqs return reqs, nil
} }
// parseResponse parses DNS answers from the returned payload // parseResponse parses DNS answers from the returned payload
+7 -1
View File
@@ -2,6 +2,7 @@ package dns
import ( import (
"math/rand" "math/rand"
"strings"
"testing" "testing"
"time" "time"
@@ -131,10 +132,15 @@ func Test_buildReqMsgs(t *testing.T) {
IPv6Enable: false, IPv6Enable: false,
FakeEnable: false, FakeEnable: false,
}, nil}, 0}, }, nil}, 0},
{"name too long", args{strings.Repeat("a", 256), dns_feature.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
}, nil}, 0},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
if got := buildReqMsgs(tt.args.domain, tt.args.option, stubID, tt.args.reqOpts); !(len(got) == tt.want) { if got, _ := buildReqMsgs(tt.args.domain, tt.args.option, stubID, tt.args.reqOpts); !(len(got) == tt.want) {
t.Errorf("buildReqMsgs() = %v, want %v", got, tt.want) t.Errorf("buildReqMsgs() = %v, want %v", got, tt.want)
} }
}) })
+20 -2
View File
@@ -137,14 +137,32 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- er
if s.Name()+"." == "DOH//"+fqdn { if s.Name()+"." == "DOH//"+fqdn {
errors.LogError(ctx, s.Name(), " tries to resolve itself! Use IP or set \"hosts\" instead") errors.LogError(ctx, s.Name(), " tries to resolve itself! Use IP or set \"hosts\" instead")
if noResponseErrCh != nil { if noResponseErrCh != nil {
noResponseErrCh <- errors.New("tries to resolve itself!", s.Name()) err := errors.New("tries to resolve itself!", s.Name())
if option.IPv4Enable {
noResponseErrCh <- err
}
if option.IPv6Enable {
noResponseErrCh <- err
}
} }
return return
} }
// As we don't want our traffic pattern looks like DoH, we use Random-Length Padding instead of Block-Length Padding recommended in RFC 8467 // As we don't want our traffic pattern looks like DoH, we use Random-Length Padding instead of Block-Length Padding recommended in RFC 8467
// Although DoH server like 1.1.1.1 will pad the response to Block-Length 468, at least it is better than no padding for response at all // Although DoH server like 1.1.1.1 will pad the response to Block-Length 468, at least it is better than no padding for response at all
reqs := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, int(crypto.RandBetween(100, 300)))) reqs, err := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, int(crypto.RandBetween(100, 300))))
if err != nil {
errors.LogErrorInner(ctx, err, "failed to build dns query for ", fqdn)
if noResponseErrCh != nil {
if option.IPv4Enable {
noResponseErrCh <- err
}
if option.IPv6Enable {
noResponseErrCh <- err
}
}
return
}
var deadline time.Time var deadline time.Time
if d, ok := ctx.Deadline(); ok { if d, ok := ctx.Deadline(); ok {
+13 -1
View File
@@ -78,7 +78,19 @@ func (s *QUICNameServer) getCacheController() *CacheController { return s.cacheC
func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) { func (s *QUICNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) {
errors.LogInfo(ctx, s.Name(), " querying: ", fqdn) errors.LogInfo(ctx, s.Name(), " querying: ", fqdn)
reqs := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0)) reqs, err := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
if err != nil {
errors.LogErrorInner(ctx, err, "failed to build dns query for ", fqdn)
if noResponseErrCh != nil {
if option.IPv4Enable {
noResponseErrCh <- err
}
if option.IPv6Enable {
noResponseErrCh <- err
}
}
return
}
var deadline time.Time var deadline time.Time
if d, ok := ctx.Deadline(); ok { if d, ok := ctx.Deadline(); ok {
+13 -1
View File
@@ -113,7 +113,19 @@ func (s *TCPNameServer) getCacheController() *CacheController {
func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) { func (s *TCPNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) {
errors.LogInfo(ctx, s.Name(), " querying DNS for: ", fqdn) errors.LogInfo(ctx, s.Name(), " querying DNS for: ", fqdn)
reqs := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0)) reqs, err := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
if err != nil {
errors.LogErrorInner(ctx, err, "failed to build dns query for ", fqdn)
if noResponseErrCh != nil {
if option.IPv4Enable {
noResponseErrCh <- err
}
if option.IPv6Enable {
noResponseErrCh <- err
}
}
return
}
var deadline time.Time var deadline time.Time
if d, ok := ctx.Deadline(); ok { if d, ok := ctx.Deadline(); ok {
+13 -1
View File
@@ -161,7 +161,19 @@ func (s *ClassicNameServer) getCacheController() *CacheController {
func (s *ClassicNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) { func (s *ClassicNameServer) sendQuery(ctx context.Context, noResponseErrCh chan<- error, fqdn string, option dns_feature.IPOption) {
errors.LogInfo(ctx, s.Name(), " querying DNS for: ", fqdn) errors.LogInfo(ctx, s.Name(), " querying DNS for: ", fqdn)
reqs := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0)) reqs, err := buildReqMsgs(fqdn, option, s.newReqID, genEDNS0Options(s.clientIP, 0))
if err != nil {
errors.LogErrorInner(ctx, err, "failed to build dns query for ", fqdn)
if noResponseErrCh != nil {
if option.IPv4Enable {
noResponseErrCh <- err
}
if option.IPv6Enable {
noResponseErrCh <- err
}
}
return
}
for _, req := range reqs { for _, req := range reqs {
udpReq := &udpDnsRequest{ udpReq := &udpDnsRequest{