修复DNS逻辑错误的问题

This commit is contained in:
CN-JS-HuiBai
2026-04-16 21:40:39 +08:00
parent 830944682f
commit 95b664a772
9 changed files with 1700 additions and 6 deletions

View File

@@ -39,13 +39,42 @@ type DNSQueryOptions struct {
ClientSubnet netip.Prefix
}
func LookupDNSTransport(manager DNSTransportManager, reference string) (DNSTransport, bool, bool) {
transport, loaded := manager.Transport(reference)
if loaded {
return transport, true, false
}
switch reference {
case C.DNSTypeLocal, C.DNSTypeFakeIP:
default:
return nil, false, false
}
var matchedTransport DNSTransport
for _, transport := range manager.Transports() {
if transport.Type() != reference {
continue
}
if matchedTransport != nil {
return nil, false, true
}
matchedTransport = transport
}
if matchedTransport != nil {
return matchedTransport, true, false
}
return nil, false, false
}
func DNSQueryOptionsFrom(ctx context.Context, options *option.DomainResolveOptions) (*DNSQueryOptions, error) {
if options == nil {
return &DNSQueryOptions{}, nil
}
transportManager := service.FromContext[DNSTransportManager](ctx)
transport, loaded := transportManager.Transport(options.Server)
transport, loaded, ambiguous := LookupDNSTransport(transportManager, options.Server)
if !loaded {
if ambiguous {
return nil, E.New("domain resolver is ambiguous: " + options.Server)
}
return nil, E.New("domain resolver not found: " + options.Server)
}
return &DNSQueryOptions{

View File

@@ -0,0 +1,122 @@
package adapter
import (
"context"
"net/netip"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
"github.com/sagernet/sing/service"
"github.com/miekg/dns"
)
type DNSRouter interface {
Lifecycle
Exchange(ctx context.Context, message *dns.Msg, options DNSQueryOptions) (*dns.Msg, error)
Lookup(ctx context.Context, domain string, options DNSQueryOptions) ([]netip.Addr, error)
ClearCache()
LookupReverseMapping(ip netip.Addr) (string, bool)
ResetNetwork()
}
type DNSClient interface {
Start()
Exchange(ctx context.Context, transport DNSTransport, message *dns.Msg, options DNSQueryOptions, responseChecker func(responseAddrs []netip.Addr) bool) (*dns.Msg, error)
Lookup(ctx context.Context, transport DNSTransport, domain string, options DNSQueryOptions, responseChecker func(responseAddrs []netip.Addr) bool) ([]netip.Addr, error)
ClearCache()
}
type DNSQueryOptions struct {
Transport DNSTransport
Strategy C.DomainStrategy
LookupStrategy C.DomainStrategy
DisableCache bool
RewriteTTL *uint32
ClientSubnet netip.Prefix
}
func LookupDNSTransport(manager DNSTransportManager, reference string) (DNSTransport, bool, bool) {
transport, loaded := manager.Transport(reference)
if loaded {
return transport, true, false
}
switch reference {
case C.DNSTypeLocal, C.DNSTypeFakeIP:
default:
return nil, false, false
}
var matchedTransport DNSTransport
for _, transport := range manager.Transports() {
if transport.Type() != reference {
continue
}
if matchedTransport != nil {
return nil, false, true
}
matchedTransport = transport
}
if matchedTransport != nil {
return matchedTransport, true, false
}
return nil, false, false
}
func DNSQueryOptionsFrom(ctx context.Context, options *option.DomainResolveOptions) (*DNSQueryOptions, error) {
if options == nil {
return &DNSQueryOptions{}, nil
}
transportManager := service.FromContext[DNSTransportManager](ctx)
transport, loaded, ambiguous := LookupDNSTransport(transportManager, options.Server)
if !loaded {
if ambiguous {
return nil, E.New("domain resolver is ambiguous: " + options.Server)
}
return nil, E.New("domain resolver not found: " + options.Server)
}
return &DNSQueryOptions{
Transport: transport,
Strategy: C.DomainStrategy(options.Strategy),
DisableCache: options.DisableCache,
RewriteTTL: options.RewriteTTL,
ClientSubnet: options.ClientSubnet.Build(netip.Prefix{}),
}, nil
}
type RDRCStore interface {
LoadRDRC(transportName string, qName string, qType uint16) (rejected bool)
SaveRDRC(transportName string, qName string, qType uint16) error
SaveRDRCAsync(transportName string, qName string, qType uint16, logger logger.Logger)
}
type DNSTransport interface {
Lifecycle
Type() string
Tag() string
Dependencies() []string
Reset()
Exchange(ctx context.Context, message *dns.Msg) (*dns.Msg, error)
}
type LegacyDNSTransport interface {
LegacyStrategy() C.DomainStrategy
LegacyClientSubnet() netip.Prefix
}
type DNSTransportRegistry interface {
option.DNSTransportOptionsRegistry
CreateDNSTransport(ctx context.Context, logger log.ContextLogger, tag string, transportType string, options any) (DNSTransport, error)
}
type DNSTransportManager interface {
Lifecycle
Transports() []DNSTransport
Transport(tag string) (DNSTransport, bool)
Default() DNSTransport
FakeIP() FakeIPTransport
Remove(tag string) error
Create(ctx context.Context, logger log.ContextLogger, tag string, outboundType string, options any) error
}

137
adapter/dns_test.go Normal file
View File

@@ -0,0 +1,137 @@
package adapter
import (
"context"
"testing"
"github.com/sagernet/sing-box/log"
"github.com/stretchr/testify/require"
mDNS "github.com/miekg/dns"
)
type testDNSTransport struct {
transportType string
tag string
}
func (t *testDNSTransport) Start(stage StartStage) error {
return nil
}
func (t *testDNSTransport) Close() error {
return nil
}
func (t *testDNSTransport) Type() string {
return t.transportType
}
func (t *testDNSTransport) Tag() string {
return t.tag
}
func (t *testDNSTransport) Dependencies() []string {
return nil
}
func (t *testDNSTransport) Reset() {
}
func (t *testDNSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
return nil, nil
}
type testDNSTransportManager struct {
transports []DNSTransport
transportByTag map[string]DNSTransport
fakeIPTransport FakeIPTransport
}
func newTestDNSTransportManager(transports ...DNSTransport) *testDNSTransportManager {
manager := &testDNSTransportManager{
transports: transports,
transportByTag: make(map[string]DNSTransport),
}
for _, transport := range transports {
manager.transportByTag[transport.Tag()] = transport
}
return manager
}
func (m *testDNSTransportManager) Start(stage StartStage) error {
return nil
}
func (m *testDNSTransportManager) Close() error {
return nil
}
func (m *testDNSTransportManager) Transports() []DNSTransport {
return m.transports
}
func (m *testDNSTransportManager) Transport(tag string) (DNSTransport, bool) {
transport, loaded := m.transportByTag[tag]
return transport, loaded
}
func (m *testDNSTransportManager) Default() DNSTransport {
return nil
}
func (m *testDNSTransportManager) FakeIP() FakeIPTransport {
return m.fakeIPTransport
}
func (m *testDNSTransportManager) Remove(tag string) error {
return nil
}
func (m *testDNSTransportManager) Create(ctx context.Context, logger log.ContextLogger, tag string, outboundType string, options any) error {
return nil
}
func TestLookupDNSTransportLocalAlias(t *testing.T) {
t.Parallel()
localTransport := &testDNSTransport{
transportType: "local",
tag: "dns-local",
}
manager := newTestDNSTransportManager(localTransport)
transport, loaded, ambiguous := LookupDNSTransport(manager, "local")
require.True(t, loaded)
require.False(t, ambiguous)
require.Same(t, localTransport, transport)
}
func TestLookupDNSTransportExactTagPreferred(t *testing.T) {
t.Parallel()
localTransport := &testDNSTransport{
transportType: "local",
tag: "local",
}
manager := newTestDNSTransportManager(localTransport)
transport, loaded, ambiguous := LookupDNSTransport(manager, "local")
require.True(t, loaded)
require.False(t, ambiguous)
require.Same(t, localTransport, transport)
}
func TestLookupDNSTransportLocalAliasAmbiguous(t *testing.T) {
t.Parallel()
manager := newTestDNSTransportManager(
&testDNSTransport{transportType: "local", tag: "dns-local-a"},
&testDNSTransport{transportType: "local", tag: "dns-local-b"},
)
transport, loaded, ambiguous := LookupDNSTransport(manager, "local")
require.Nil(t, transport)
require.False(t, loaded)
require.True(t, ambiguous)
}