mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-10-13 17:44:00 +00:00
refactor: Async DNS
This commit is contained in:
+38
-40
@@ -40,28 +40,30 @@ func HandleStreamDNSRequest(ctx context.Context, router adapter.DNSRouter, conn
|
||||
return err
|
||||
}
|
||||
metadataInQuery := metadata
|
||||
go func() error {
|
||||
response, err := router.Exchange(adapter.WithContext(ctx, &metadataInQuery), &message, adapter.DNSQueryOptions{})
|
||||
router.ExchangeAsync(adapter.WithContext(ctx, &metadataInQuery), &message, adapter.DNSQueryOptions{}, func(response *mDNS.Msg, err error) {
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return err
|
||||
return
|
||||
}
|
||||
responseLength := response.Len()
|
||||
responseBuffer := buf.NewSize(3 + responseLength)
|
||||
defer responseBuffer.Release()
|
||||
responseBuffer.Resize(2, 0)
|
||||
n, err := response.PackBuffer(responseBuffer.FreeBytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
responseBuffer.Truncate(len(n))
|
||||
binary.BigEndian.PutUint16(responseBuffer.ExtendHeader(2), uint16(len(n)))
|
||||
_, err = conn.Write(responseBuffer.Bytes())
|
||||
return err
|
||||
}()
|
||||
go writeStreamResponse(conn, response)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeStreamResponse(conn net.Conn, response *mDNS.Msg) {
|
||||
responseLength := response.Len()
|
||||
responseBuffer := buf.NewSize(3 + responseLength)
|
||||
defer responseBuffer.Release()
|
||||
responseBuffer.Resize(2, 0)
|
||||
n, err := response.PackBuffer(responseBuffer.FreeBytes())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
responseBuffer.Truncate(len(n))
|
||||
binary.BigEndian.PutUint16(responseBuffer.ExtendHeader(2), uint16(len(n)))
|
||||
conn.Write(responseBuffer.Bytes())
|
||||
}
|
||||
|
||||
func NewDNSPacketConnection(ctx context.Context, router adapter.DNSRouter, conn N.PacketConn, cachedPackets []*N.PacketBuffer, metadata adapter.InboundContext) error {
|
||||
metadata.Destination = M.Socksaddr{}
|
||||
var reader N.PacketReader = conn
|
||||
@@ -123,24 +125,22 @@ func NewDNSPacketConnection(ctx context.Context, router adapter.DNSRouter, conn
|
||||
timeout.Update()
|
||||
}
|
||||
metadataInQuery := metadata
|
||||
go func() error {
|
||||
response, err := router.Exchange(adapter.WithContext(ctx, &metadataInQuery), &message, adapter.DNSQueryOptions{})
|
||||
router.ExchangeAsync(adapter.WithContext(ctx, &metadataInQuery), &message, adapter.DNSQueryOptions{}, func(response *mDNS.Msg, err error) {
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
return err
|
||||
return
|
||||
}
|
||||
timeout.Update()
|
||||
responseBuffer, err := dns.TruncateDNSMessage(&message, response, 1024)
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
return err
|
||||
responseBuffer, truncateErr := dns.TruncateDNSMessage(&message, response, 1024)
|
||||
if truncateErr != nil {
|
||||
cancel(truncateErr)
|
||||
return
|
||||
}
|
||||
err = conn.WritePacket(responseBuffer, destination)
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
writeErr := conn.WritePacket(responseBuffer, destination)
|
||||
if writeErr != nil {
|
||||
cancel(writeErr)
|
||||
}
|
||||
return err
|
||||
}()
|
||||
})
|
||||
}
|
||||
})
|
||||
group.Cleanup(func() {
|
||||
@@ -193,24 +193,22 @@ func newDNSPacketConnection(ctx context.Context, router adapter.DNSRouter, conn
|
||||
timeout.Update()
|
||||
}
|
||||
metadataInQuery := metadata
|
||||
go func() error {
|
||||
response, err := router.Exchange(adapter.WithContext(ctx, &metadataInQuery), &message, adapter.DNSQueryOptions{})
|
||||
router.ExchangeAsync(adapter.WithContext(ctx, &metadataInQuery), &message, adapter.DNSQueryOptions{}, func(response *mDNS.Msg, err error) {
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
return err
|
||||
return
|
||||
}
|
||||
timeout.Update()
|
||||
responseBuffer, err := dns.TruncateDNSMessage(&message, response, 1024)
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
return err
|
||||
responseBuffer, truncateErr := dns.TruncateDNSMessage(&message, response, 1024)
|
||||
if truncateErr != nil {
|
||||
cancel(truncateErr)
|
||||
return
|
||||
}
|
||||
err = conn.WritePacket(responseBuffer, destination)
|
||||
if err != nil {
|
||||
cancel(err)
|
||||
writeErr := conn.WritePacket(responseBuffer, destination)
|
||||
if writeErr != nil {
|
||||
cancel(writeErr)
|
||||
}
|
||||
return err
|
||||
}()
|
||||
})
|
||||
}
|
||||
})
|
||||
group.Cleanup(func() {
|
||||
|
||||
@@ -4,7 +4,6 @@ package tailscale
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
@@ -276,48 +275,64 @@ func (t *DNSTransport) PreferredDomain(domain string) bool {
|
||||
}
|
||||
|
||||
func (t *DNSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
done := make(chan struct{})
|
||||
var (
|
||||
response *mDNS.Msg
|
||||
err error
|
||||
)
|
||||
t.ExchangeAsync(ctx, message, func(callbackResponse *mDNS.Msg, callbackErr error) {
|
||||
response = callbackResponse
|
||||
err = callbackErr
|
||||
close(done)
|
||||
})
|
||||
<-done
|
||||
return response, err
|
||||
}
|
||||
|
||||
func (t *DNSTransport) ExchangeAsync(ctx context.Context, message *mDNS.Msg, callback func(response *mDNS.Msg, err error)) {
|
||||
if len(message.Question) != 1 {
|
||||
return nil, os.ErrInvalid
|
||||
callback(nil, os.ErrInvalid)
|
||||
return
|
||||
}
|
||||
if t.acceptSearchDomain && mDNS.CountLabel(message.Question[0].Name) == 1 {
|
||||
return t.exchangeWithSearchDomains(ctx, message)
|
||||
t.exchangeWithSearchDomains(ctx, message, callback)
|
||||
return
|
||||
}
|
||||
t.access.RLock()
|
||||
acceptDefaultResolvers := t.acceptDefaultResolvers
|
||||
t.access.RUnlock()
|
||||
return t.exchangeOnce(ctx, message, acceptDefaultResolvers)
|
||||
t.exchangeOnce(ctx, message, acceptDefaultResolvers, callback)
|
||||
}
|
||||
|
||||
func (t *DNSTransport) exchangeWithSearchDomains(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||
func (t *DNSTransport) exchangeWithSearchDomains(ctx context.Context, message *mDNS.Msg, callback func(response *mDNS.Msg, err error)) {
|
||||
t.access.RLock()
|
||||
searchDomains := t.searchDomains
|
||||
t.access.RUnlock()
|
||||
if len(searchDomains) == 0 {
|
||||
callback(nil, dns.RcodeNameError)
|
||||
return
|
||||
}
|
||||
originalQuestion := message.Question[0]
|
||||
singleLabel := strings.TrimSuffix(originalQuestion.Name, ".")
|
||||
var lastErr error
|
||||
domainExchangers := make([]transport.AsyncExchanger, 0, len(searchDomains))
|
||||
for _, searchDomain := range searchDomains {
|
||||
expandedName := singleLabel + "." + searchDomain
|
||||
question := originalQuestion
|
||||
question.Name = expandedName
|
||||
rewritten := *message
|
||||
rewritten.Question = []mDNS.Question{question}
|
||||
response, err := t.exchangeOnce(ctx, &rewritten, false)
|
||||
if err == nil {
|
||||
if response.Rcode == mDNS.RcodeNameError {
|
||||
continue
|
||||
}
|
||||
restoreOriginalQuestion(response, expandedName, originalQuestion)
|
||||
return response, nil
|
||||
}
|
||||
if errors.Is(err, dns.RcodeNameError) {
|
||||
continue
|
||||
}
|
||||
lastErr = err
|
||||
domainExchangers = append(domainExchangers, func(exchangeCtx context.Context, exchangeCallback func(response *mDNS.Msg, err error)) {
|
||||
question := originalQuestion
|
||||
question.Name = expandedName
|
||||
rewritten := *message
|
||||
rewritten.Question = []mDNS.Question{question}
|
||||
t.exchangeOnce(exchangeCtx, &rewritten, false, func(response *mDNS.Msg, err error) {
|
||||
if err == nil {
|
||||
restoreOriginalQuestion(response, expandedName, originalQuestion)
|
||||
}
|
||||
exchangeCallback(response, err)
|
||||
})
|
||||
})
|
||||
}
|
||||
if lastErr != nil {
|
||||
return nil, lastErr
|
||||
}
|
||||
return nil, dns.RcodeNameError
|
||||
transport.ExchangeSequential(ctx, domainExchangers, func(response *mDNS.Msg, err error) bool {
|
||||
return err == nil && response.Rcode != mDNS.RcodeNameError
|
||||
}, callback)
|
||||
}
|
||||
|
||||
// RFC 1035 §4.1.1 requires the response Question to match the request byte-for-byte,
|
||||
@@ -331,7 +346,7 @@ func restoreOriginalQuestion(response *mDNS.Msg, expandedName string, originalQu
|
||||
}
|
||||
}
|
||||
|
||||
func (t *DNSTransport) exchangeOnce(ctx context.Context, message *mDNS.Msg, allowDefaultResolvers bool) (*mDNS.Msg, error) {
|
||||
func (t *DNSTransport) exchangeOnce(ctx context.Context, message *mDNS.Msg, allowDefaultResolvers bool, callback func(response *mDNS.Msg, err error)) {
|
||||
question := message.Question[0]
|
||||
|
||||
t.access.RLock()
|
||||
@@ -348,58 +363,53 @@ func (t *DNSTransport) exchangeOnce(ctx context.Context, message *mDNS.Msg, allo
|
||||
return addr.Is4()
|
||||
})
|
||||
if len(addresses4) > 0 {
|
||||
return dns.FixedResponse(message.Id, question, addresses4, C.DefaultDNSTTL), nil
|
||||
callback(dns.FixedResponse(message.Id, question, addresses4, C.DefaultDNSTTL), nil)
|
||||
return
|
||||
}
|
||||
case mDNS.TypeAAAA:
|
||||
addresses6 := common.Filter(addresses, func(addr netip.Addr) bool {
|
||||
return addr.Is6()
|
||||
})
|
||||
if len(addresses6) > 0 {
|
||||
return dns.FixedResponse(message.Id, question, addresses6, C.DefaultDNSTTL), nil
|
||||
callback(dns.FixedResponse(message.Id, question, addresses6, C.DefaultDNSTTL), nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
for domainSuffix, transports := range routes {
|
||||
if mDNS.IsSubDomain(domainSuffix, question.Name) {
|
||||
if len(transports) == 0 {
|
||||
return &mDNS.Msg{
|
||||
callback(&mDNS.Msg{
|
||||
MsgHdr: mDNS.MsgHdr{
|
||||
Id: message.Id,
|
||||
Rcode: mDNS.RcodeNameError,
|
||||
Response: true,
|
||||
},
|
||||
Question: []mDNS.Question{question},
|
||||
}, nil
|
||||
}, nil)
|
||||
return
|
||||
}
|
||||
var lastErr error
|
||||
for _, dnsTransport := range transports {
|
||||
response, err := dnsTransport.Exchange(ctx, message)
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
continue
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
return nil, lastErr
|
||||
transport.ExchangeSequential(ctx, resolverExchangers(transports, message), nil, callback)
|
||||
return
|
||||
}
|
||||
}
|
||||
if allowDefaultResolvers {
|
||||
if len(defaultResolvers) > 0 {
|
||||
var lastErr error
|
||||
for _, resolver := range defaultResolvers {
|
||||
response, err := resolver.Exchange(ctx, message)
|
||||
if err != nil {
|
||||
lastErr = err
|
||||
continue
|
||||
}
|
||||
return response, nil
|
||||
}
|
||||
return nil, lastErr
|
||||
transport.ExchangeSequential(ctx, resolverExchangers(defaultResolvers, message), nil, callback)
|
||||
} else {
|
||||
return nil, E.New("missing default resolvers")
|
||||
callback(nil, E.New("missing default resolvers"))
|
||||
}
|
||||
return
|
||||
}
|
||||
return nil, dns.RcodeNameError
|
||||
callback(nil, dns.RcodeNameError)
|
||||
}
|
||||
|
||||
func resolverExchangers(resolvers []adapter.DNSTransport, message *mDNS.Msg) []transport.AsyncExchanger {
|
||||
return common.Map(resolvers, func(resolver adapter.DNSTransport) transport.AsyncExchanger {
|
||||
return func(ctx context.Context, callback func(response *mDNS.Msg, err error)) {
|
||||
resolver.ExchangeAsync(ctx, message, callback)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (t *DNSTransport) collectResolversLocked() []adapter.DNSTransport {
|
||||
|
||||
Reference in New Issue
Block a user