refactor: Async DNS

This commit is contained in:
世界
2026-08-30 17:41:43 +08:00
parent 90d75b9673
commit 5d424ea2ac
29 changed files with 1956 additions and 994 deletions
+38 -40
View File
@@ -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() {
+63 -53
View File
@@ -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 {