dns: Fix preferred_by local support for platform local transport

This commit is contained in:
NagaseMinato
2026-08-30 17:41:45 +08:00
committed by 世界
parent ec3e95935f
commit eb6db316cc
5 changed files with 134 additions and 63 deletions
+22 -47
View File
@@ -8,7 +8,6 @@ import (
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/dns"
"github.com/sagernet/sing-box/dns/transport/hosts"
"github.com/sagernet/sing-box/dns/transport/mdns"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
@@ -33,21 +32,18 @@ var (
type Transport struct {
dns.TransportAdapter
ctx context.Context
logger logger.ContextLogger
hosts *hosts.File
dialer N.Dialer
preferGo bool
fallback bool
resolved ResolvedResolver
mdnsTransport adapter.DNSTransport
dhcpTransport dhcpTransport
system systemResolver
serverSet atomic.Pointer[localServerSet]
serverSetAccess sync.Mutex
neighborResolver adapter.NeighborResolver
neighborSuffixes []string
ctx context.Context
logger logger.ContextLogger
preferredResolver *PreferredDomainResolver
dialer N.Dialer
preferGo bool
fallback bool
resolved ResolvedResolver
mdnsTransport adapter.DNSTransport
dhcpTransport dhcpTransport
system systemResolver
serverSet atomic.Pointer[localServerSet]
serverSetAccess sync.Mutex
}
type dhcpTransport interface {
@@ -60,29 +56,24 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt
if err != nil {
return nil, err
}
suffixes, err := buildNeighborMatchers(options.NeighborDomain)
preferredResolver, err := NewPreferredDomainResolver(ctx, logger, options)
if err != nil {
return nil, err
}
return &Transport{
TransportAdapter: dns.NewTransportAdapterWithLocalOptions(C.DNSTypeLocal, tag, options),
ctx: ctx,
logger: logger,
dialer: transportDialer,
preferGo: options.PreferGo,
neighborSuffixes: suffixes,
TransportAdapter: dns.NewTransportAdapterWithLocalOptions(C.DNSTypeLocal, tag, options),
ctx: ctx,
logger: logger,
preferredResolver: preferredResolver,
dialer: transportDialer,
preferGo: options.PreferGo,
}, nil
}
func (t *Transport) Start(stage adapter.StartStage) error {
t.preferredResolver.Start(stage)
switch stage {
case adapter.StartStateInitialize:
defaultHosts, err := hosts.NewDefault()
if err != nil {
t.logger.Warn(err)
} else {
t.hosts = defaultHosts
}
if !t.preferGo && isSystemdResolvedManaged() {
resolvedResolver, err := NewResolvedResolver(t.ctx, t.logger)
if err == nil {
@@ -109,10 +100,6 @@ func (t *Transport) Start(stage adapter.StartStage) error {
} else {
t.mdnsTransport = mdns.NewRawTransport(t.TransportAdapter, t.ctx, t.logger)
}
router := service.FromContext[adapter.Router](t.ctx)
if router != nil {
t.neighborResolver = router.NeighborResolver()
}
fallthrough
default:
if t.dhcpTransport != nil {
@@ -160,12 +147,7 @@ func (t *Transport) Reset() {
}
func (t *Transport) PreferredDomain(domain string) bool {
if t.hosts != nil {
if len(t.hosts.Lookup(dns.FqdnToDomain(domain))) > 0 {
return true
}
}
return t.hasNeighborHost(domain) || mdns.IsLocalDomain(domain)
return t.preferredResolver.PreferredDomain(domain)
}
func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
@@ -185,14 +167,7 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
func (t *Transport) ExchangeAsync(ctx context.Context, message *mDNS.Msg, callback func(response *mDNS.Msg, err error)) {
question := message.Question[0]
if t.hosts != nil && (question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA) {
addresses := t.hosts.Lookup(dns.FqdnToDomain(question.Name))
if len(addresses) > 0 {
callback(dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil)
return
}
}
response := t.lookupNeighbor(message)
response := t.preferredResolver.Lookup(message)
if response != nil {
callback(response, nil)
return
+8 -8
View File
@@ -24,34 +24,34 @@ func buildNeighborMatchers(domains []string) ([]string, error) {
return suffixes, nil
}
func (t *Transport) lookupNeighbor(message *mDNS.Msg) *mDNS.Msg {
if t.neighborResolver == nil {
func (r *PreferredDomainResolver) lookupNeighbor(message *mDNS.Msg) *mDNS.Msg {
if r.neighborResolver == nil {
return nil
}
question := message.Question[0]
if question.Qtype != mDNS.TypeA && question.Qtype != mDNS.TypeAAAA {
return nil
}
host := extractNeighborHost(mDNS.CanonicalName(question.Name), t.neighborSuffixes)
host := extractNeighborHost(mDNS.CanonicalName(question.Name), r.neighborSuffixes)
if host == "" {
return nil
}
addresses := t.neighborResolver.LookupAddresses(host)
addresses := r.neighborResolver.LookupAddresses(host)
if len(addresses) == 0 {
return nil
}
return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL)
}
func (t *Transport) hasNeighborHost(domain string) bool {
if t.neighborResolver == nil {
func (r *PreferredDomainResolver) hasNeighborHost(domain string) bool {
if r.neighborResolver == nil {
return false
}
host := extractNeighborHost(domain, t.neighborSuffixes)
host := extractNeighborHost(domain, r.neighborSuffixes)
if host == "" {
return false
}
return len(t.neighborResolver.LookupAddresses(host)) > 0
return len(r.neighborResolver.LookupAddresses(host)) > 0
}
func extractNeighborHost(canonical string, suffixes []string) string {
+76
View File
@@ -0,0 +1,76 @@
package local
import (
"context"
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/dns"
"github.com/sagernet/sing-box/dns/transport/hosts"
"github.com/sagernet/sing-box/dns/transport/mdns"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common/logger"
"github.com/sagernet/sing/service"
mDNS "github.com/miekg/dns"
)
type PreferredDomainResolver struct {
ctx context.Context
logger logger.ContextLogger
hosts *hosts.File
neighborResolver adapter.NeighborResolver
neighborSuffixes []string
}
func NewPreferredDomainResolver(ctx context.Context, contextLogger logger.ContextLogger, options option.LocalDNSServerOptions) (*PreferredDomainResolver, error) {
suffixes, err := buildNeighborMatchers(options.NeighborDomain)
if err != nil {
return nil, err
}
return &PreferredDomainResolver{
ctx: ctx,
logger: contextLogger,
neighborSuffixes: suffixes,
}, nil
}
func (r *PreferredDomainResolver) Start(stage adapter.StartStage) {
switch stage {
case adapter.StartStateInitialize:
defaultHosts, err := hosts.NewDefault()
if err != nil {
r.logger.Warn(err)
} else {
r.hosts = defaultHosts
}
case adapter.StartStateStart:
router := service.FromContext[adapter.Router](r.ctx)
if router != nil {
r.neighborResolver = router.NeighborResolver()
}
}
}
func (r *PreferredDomainResolver) PreferredDomain(domain string) bool {
if r.hosts != nil {
if len(r.hosts.Lookup(dns.FqdnToDomain(domain))) > 0 {
return true
}
}
return r.hasNeighborHost(domain) || mdns.IsLocalDomain(domain)
}
func (r *PreferredDomainResolver) Lookup(message *mDNS.Msg) *mDNS.Msg {
question := message.Question[0]
if question.Qtype != mDNS.TypeA && question.Qtype != mDNS.TypeAAAA {
return nil
}
if r.hosts != nil {
addresses := r.hosts.Lookup(dns.FqdnToDomain(question.Name))
if len(addresses) > 0 {
return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL)
}
}
return r.lookupNeighbor(message)
}