From 19d1dd09b87f10cb651bc5b1ee137a5ab5c1c61e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Sun, 12 Jan 2025 12:45:27 +0800 Subject: [PATCH] refactor: Outbound domain resolver --- adapter/network.go | 14 ++-- box.go | 2 +- common/dialer/dialer.go | 96 +++++++++++++++++----------- common/dialer/resolve.go | 58 ++++++++++++----- common/tls/reality_server.go | 2 +- dns/transport_dialer.go | 14 +++- experimental/deprecated/constants.go | 18 ++++++ option/outbound.go | 40 +++++++++++- option/route.go | 1 + protocol/direct/outbound.go | 12 ++-- protocol/http/outbound.go | 2 +- protocol/hysteria/outbound.go | 2 +- protocol/hysteria2/outbound.go | 2 +- protocol/shadowsocks/outbound.go | 2 +- protocol/shadowtls/inbound.go | 4 +- protocol/shadowtls/outbound.go | 2 +- protocol/socks/outbound.go | 2 +- protocol/ssh/outbound.go | 2 +- protocol/tor/outbound.go | 2 +- protocol/trojan/outbound.go | 2 +- protocol/tuic/outbound.go | 2 +- protocol/vless/outbound.go | 2 +- protocol/vmess/outbound.go | 2 +- protocol/wireguard/endpoint.go | 6 +- protocol/wireguard/outbound.go | 6 +- route/network.go | 13 +++- route/rule/rule_action.go | 2 +- route/rule/rule_dns.go | 2 +- route/rule/rule_item_outbound.go | 9 ++- 29 files changed, 224 insertions(+), 99 deletions(-) diff --git a/adapter/network.go b/adapter/network.go index 00ef54b8..a1b6f383 100644 --- a/adapter/network.go +++ b/adapter/network.go @@ -28,12 +28,14 @@ type NetworkManager interface { } type NetworkOptions struct { - NetworkStrategy *C.NetworkStrategy - NetworkType []C.InterfaceType - FallbackNetworkType []C.InterfaceType - FallbackDelay time.Duration - BindInterface string - RoutingMark uint32 + BindInterface string + RoutingMark uint32 + DomainResolver string + DomainResolveOptions DNSQueryOptions + NetworkStrategy *C.NetworkStrategy + NetworkType []C.InterfaceType + FallbackNetworkType []C.InterfaceType + FallbackDelay time.Duration } type InterfaceUpdateListener interface { diff --git a/box.go b/box.go index bd15c66a..1f8564f2 100644 --- a/box.go +++ b/box.go @@ -309,7 +309,7 @@ func New(options Options) (*Box, error) { } } if ntpOptions.Enabled { - ntpDialer, err := dialer.New(ctx, ntpOptions.DialerOptions) + ntpDialer, err := dialer.New(ctx, ntpOptions.DialerOptions, ntpOptions.ServerIsDomain()) if err != nil { return nil, E.Cause(err, "create NTP service") } diff --git a/common/dialer/dialer.go b/common/dialer/dialer.go index f63e3864..e860f520 100644 --- a/common/dialer/dialer.go +++ b/common/dialer/dialer.go @@ -8,6 +8,7 @@ import ( "github.com/sagernet/sing-box/adapter" C "github.com/sagernet/sing-box/constant" + "github.com/sagernet/sing-box/experimental/deprecated" "github.com/sagernet/sing-box/option" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" @@ -15,7 +16,7 @@ import ( "github.com/sagernet/sing/service" ) -func New(ctx context.Context, options option.DialerOptions) (N.Dialer, error) { +func New(ctx context.Context, options option.DialerOptions, remoteIsDomain bool) (N.Dialer, error) { if options.IsWireGuardListener { return NewDefault(ctx, options) } @@ -23,52 +24,73 @@ func New(ctx context.Context, options option.DialerOptions) (N.Dialer, error) { dialer N.Dialer err error ) - if options.Detour == "" { - dialer, err = NewDefault(ctx, options) - if err != nil { - return nil, err - } - } else { + if options.Detour != "" { outboundManager := service.FromContext[adapter.OutboundManager](ctx) if outboundManager == nil { return nil, E.New("missing outbound manager") } dialer = NewDetour(outboundManager, options.Detour) - } - if options.Detour == "" { - router := service.FromContext[adapter.DNSRouter](ctx) - if router != nil { - dialer = NewResolveDialer( - router, - dialer, - options.Detour == "" && !options.TCPFastOpen, - C.DomainStrategy(options.DomainStrategy), - time.Duration(options.FallbackDelay)) + } else { + dialer, err = NewDefault(ctx, options) + if err != nil { + return nil, err } } + if remoteIsDomain && options.Detour == "" { + networkManager := service.FromContext[adapter.NetworkManager](ctx) + dnsTransport := service.FromContext[adapter.DNSTransportManager](ctx) + var defaultOptions adapter.NetworkOptions + if networkManager != nil { + defaultOptions = networkManager.DefaultOptions() + } + var ( + dnsQueryOptions adapter.DNSQueryOptions + resolveFallbackDelay time.Duration + ) + if options.DomainResolver != nil && options.DomainResolver.Server != "" { + transport, loaded := dnsTransport.Transport(options.DomainResolver.Server) + if !loaded { + return nil, E.New("domain resolver not found: " + options.DomainResolver.Server) + } + var strategy C.DomainStrategy + if options.DomainResolver.Strategy != option.DomainStrategy(C.DomainStrategyAsIS) { + strategy = C.DomainStrategy(options.DomainResolver.Strategy) + } else if + //nolint:staticcheck + options.DomainStrategy != option.DomainStrategy(C.DomainStrategyAsIS) { + //nolint:staticcheck + strategy = C.DomainStrategy(options.DomainStrategy) + } + dnsQueryOptions = adapter.DNSQueryOptions{ + Transport: transport, + Strategy: strategy, + DisableCache: options.DomainResolver.DisableCache, + RewriteTTL: options.DomainResolver.RewriteTTL, + ClientSubnet: options.DomainResolver.ClientSubnet.Build(netip.Prefix{}), + } + resolveFallbackDelay = time.Duration(options.FallbackDelay) + } else if defaultOptions.DomainResolver != "" { + dnsQueryOptions = defaultOptions.DomainResolveOptions + transport, loaded := dnsTransport.Transport(defaultOptions.DomainResolver) + if !loaded { + return nil, E.New("default domain resolver not found: " + defaultOptions.DomainResolver) + } + dnsQueryOptions.Transport = transport + resolveFallbackDelay = time.Duration(options.FallbackDelay) + } else { + deprecated.Report(ctx, deprecated.OptionMissingDomainResolver) + } + dialer = NewResolveDialer( + ctx, + dialer, + options.Detour == "" && !options.TCPFastOpen, + dnsQueryOptions, + resolveFallbackDelay, + ) + } return dialer, nil } -func NewDirect(ctx context.Context, options option.DialerOptions) (ParallelInterfaceDialer, error) { - if options.Detour != "" { - return nil, E.New("`detour` is not supported in direct context") - } - if options.IsWireGuardListener { - return NewDefault(ctx, options) - } - dialer, err := NewDefault(ctx, options) - if err != nil { - return nil, err - } - return NewResolveParallelInterfaceDialer( - service.FromContext[adapter.DNSRouter](ctx), - dialer, - true, - C.DomainStrategy(options.DomainStrategy), - time.Duration(options.FallbackDelay), - ), nil -} - type ParallelInterfaceDialer interface { N.Dialer DialParallelInterface(ctx context.Context, network string, destination M.Socksaddr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) diff --git a/common/dialer/resolve.go b/common/dialer/resolve.go index 3d667a6c..b28b9ebd 100644 --- a/common/dialer/resolve.go +++ b/common/dialer/resolve.go @@ -11,6 +11,7 @@ import ( "github.com/sagernet/sing/common/bufio" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/service" ) var ( @@ -18,20 +19,30 @@ var ( _ ParallelInterfaceDialer = (*resolveParallelNetworkDialer)(nil) ) +type ResolveDialer interface { + N.Dialer + QueryOptions() adapter.DNSQueryOptions +} + +type ParallelInterfaceResolveDialer interface { + ParallelInterfaceDialer + QueryOptions() adapter.DNSQueryOptions +} + type resolveDialer struct { + router adapter.DNSRouter dialer N.Dialer parallel bool - router adapter.DNSRouter - strategy C.DomainStrategy + queryOptions adapter.DNSQueryOptions fallbackDelay time.Duration } -func NewResolveDialer(router adapter.DNSRouter, dialer N.Dialer, parallel bool, strategy C.DomainStrategy, fallbackDelay time.Duration) N.Dialer { +func NewResolveDialer(ctx context.Context, dialer N.Dialer, parallel bool, queryOptions adapter.DNSQueryOptions, fallbackDelay time.Duration) ResolveDialer { return &resolveDialer{ + service.FromContext[adapter.DNSRouter](ctx), dialer, parallel, - router, - strategy, + queryOptions, fallbackDelay, } } @@ -41,13 +52,13 @@ type resolveParallelNetworkDialer struct { dialer ParallelInterfaceDialer } -func NewResolveParallelInterfaceDialer(router adapter.DNSRouter, dialer ParallelInterfaceDialer, parallel bool, strategy C.DomainStrategy, fallbackDelay time.Duration) ParallelInterfaceDialer { +func NewResolveParallelInterfaceDialer(ctx context.Context, dialer ParallelInterfaceDialer, parallel bool, queryOptions adapter.DNSQueryOptions, fallbackDelay time.Duration) ParallelInterfaceResolveDialer { return &resolveParallelNetworkDialer{ resolveDialer{ + service.FromContext[adapter.DNSRouter](ctx), dialer, parallel, - router, - strategy, + queryOptions, fallbackDelay, }, dialer, @@ -59,12 +70,12 @@ func (d *resolveDialer) DialContext(ctx context.Context, network string, destina return d.dialer.DialContext(ctx, network, destination) } ctx = log.ContextWithOverrideLevel(ctx, log.LevelDebug) - addresses, err := d.router.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{Strategy: d.strategy}) + addresses, err := d.router.Lookup(ctx, destination.Fqdn, d.queryOptions) if err != nil { return nil, err } if d.parallel { - return N.DialParallel(ctx, d.dialer, network, destination, addresses, d.strategy == C.DomainStrategyPreferIPv6, d.fallbackDelay) + return N.DialParallel(ctx, d.dialer, network, destination, addresses, d.queryOptions.Strategy == C.DomainStrategyPreferIPv6, d.fallbackDelay) } else { return N.DialSerial(ctx, d.dialer, network, destination, addresses) } @@ -75,7 +86,7 @@ func (d *resolveDialer) ListenPacket(ctx context.Context, destination M.Socksadd return d.dialer.ListenPacket(ctx, destination) } ctx = log.ContextWithOverrideLevel(ctx, log.LevelDebug) - addresses, err := d.router.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{Strategy: d.strategy}) + addresses, err := d.router.Lookup(ctx, destination.Fqdn, d.queryOptions) if err != nil { return nil, err } @@ -86,14 +97,20 @@ func (d *resolveDialer) ListenPacket(ctx context.Context, destination M.Socksadd return bufio.NewNATPacketConn(bufio.NewPacketConn(conn), M.SocksaddrFrom(destinationAddress, destination.Port), destination), nil } +func (d *resolveDialer) QueryOptions() adapter.DNSQueryOptions { + return d.queryOptions +} + +func (d *resolveDialer) Upstream() any { + return d.dialer +} + func (d *resolveParallelNetworkDialer) DialParallelInterface(ctx context.Context, network string, destination M.Socksaddr, strategy *C.NetworkStrategy, interfaceType []C.InterfaceType, fallbackInterfaceType []C.InterfaceType, fallbackDelay time.Duration) (net.Conn, error) { if !destination.IsFqdn() { return d.dialer.DialContext(ctx, network, destination) } ctx = log.ContextWithOverrideLevel(ctx, log.LevelDebug) - addresses, err := d.router.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{ - Strategy: d.strategy, - }) + addresses, err := d.router.Lookup(ctx, destination.Fqdn, d.queryOptions) if err != nil { return nil, err } @@ -101,7 +118,7 @@ func (d *resolveParallelNetworkDialer) DialParallelInterface(ctx context.Context fallbackDelay = d.fallbackDelay } if d.parallel { - return DialParallelNetwork(ctx, d.dialer, network, destination, addresses, d.strategy == C.DomainStrategyPreferIPv6, strategy, interfaceType, fallbackInterfaceType, fallbackDelay) + return DialParallelNetwork(ctx, d.dialer, network, destination, addresses, d.queryOptions.Strategy == C.DomainStrategyPreferIPv6, strategy, interfaceType, fallbackInterfaceType, fallbackDelay) } else { return DialSerialNetwork(ctx, d.dialer, network, destination, addresses, strategy, interfaceType, fallbackInterfaceType, fallbackDelay) } @@ -112,10 +129,13 @@ func (d *resolveParallelNetworkDialer) ListenSerialInterfacePacket(ctx context.C return d.dialer.ListenPacket(ctx, destination) } ctx = log.ContextWithOverrideLevel(ctx, log.LevelDebug) - addresses, err := d.router.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{Strategy: d.strategy}) + addresses, err := d.router.Lookup(ctx, destination.Fqdn, d.queryOptions) if err != nil { return nil, err } + if fallbackDelay == 0 { + fallbackDelay = d.fallbackDelay + } conn, destinationAddress, err := ListenSerialNetworkPacket(ctx, d.dialer, destination, addresses, strategy, interfaceType, fallbackInterfaceType, fallbackDelay) if err != nil { return nil, err @@ -123,6 +143,10 @@ func (d *resolveParallelNetworkDialer) ListenSerialInterfacePacket(ctx context.C return bufio.NewNATPacketConn(bufio.NewPacketConn(conn), M.SocksaddrFrom(destinationAddress, destination.Port), destination), nil } -func (d *resolveDialer) Upstream() any { +func (d *resolveParallelNetworkDialer) QueryOptions() adapter.DNSQueryOptions { + return d.queryOptions +} + +func (d *resolveParallelNetworkDialer) Upstream() any { return d.dialer } diff --git a/common/tls/reality_server.go b/common/tls/reality_server.go index cf429815..912d13dd 100644 --- a/common/tls/reality_server.go +++ b/common/tls/reality_server.go @@ -101,7 +101,7 @@ func NewRealityServer(ctx context.Context, logger log.Logger, options option.Inb tlsConfig.ShortIds[shortID] = true } - handshakeDialer, err := dialer.New(ctx, options.Reality.Handshake.DialerOptions) + handshakeDialer, err := dialer.New(ctx, options.Reality.Handshake.DialerOptions, options.Reality.Handshake.ServerIsDomain()) if err != nil { return nil, err } diff --git a/dns/transport_dialer.go b/dns/transport_dialer.go index 14e1188d..d9298b7f 100644 --- a/dns/transport_dialer.go +++ b/dns/transport_dialer.go @@ -19,12 +19,20 @@ func NewLocalDialer(ctx context.Context, options option.LocalDNSServerOptions) ( if options.LegacyDefaultDialer { return dialer.NewDefaultOutbound(ctx), nil } else { - return dialer.New(ctx, options.DialerOptions) + return dialer.New(ctx, options.DialerOptions, false) } } func NewRemoteDialer(ctx context.Context, options option.RemoteDNSServerOptions) (N.Dialer, error) { - transportDialer, err := NewLocalDialer(ctx, options.LocalDNSServerOptions) + var ( + transportDialer N.Dialer + err error + ) + if options.LegacyDefaultDialer { + transportDialer = dialer.NewDefaultOutbound(ctx) + } else { + transportDialer, err = dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) + } if err != nil { return nil, err } @@ -35,7 +43,7 @@ func NewRemoteDialer(ctx context.Context, options option.RemoteDNSServerOptions) return nil, E.New("address resolver not found: ", options.AddressResolver) } transportDialer = NewTransportDialer(transportDialer, service.FromContext[adapter.DNSRouter](ctx), resolverTransport, C.DomainStrategy(options.AddressStrategy), time.Duration(options.AddressFallbackDelay)) - } else if M.IsDomainName(options.Server) { + } else if options.ServerIsDomain() { return nil, E.New("missing address resolver for server: ", options.Server) } return transportDialer, nil diff --git a/experimental/deprecated/constants.go b/experimental/deprecated/constants.go index bf648365..270eda65 100644 --- a/experimental/deprecated/constants.go +++ b/experimental/deprecated/constants.go @@ -161,6 +161,20 @@ var OptionLegacyDNSFakeIPOptions = Note{ ScheduledVersion: "1.14.0", } +var OptionOutboundDNSRuleItem = Note{ + Name: "outbound-dns-rule-item", + Description: "outbound DNS rule item", + DeprecatedVersion: "1.12.0", + ScheduledVersion: "1.14.0", +} + +var OptionMissingDomainResolver = Note{ + Name: "missing-domain-resolver", + Description: "missing `route.default_domain_resolver` or `domain_resolver` in dial fields", + DeprecatedVersion: "1.12.0", + ScheduledVersion: "1.14.0", +} + var Options = []Note{ OptionBadMatchSource, OptionGEOIP, @@ -172,4 +186,8 @@ var Options = []Note{ OptionWireGuardOutbound, OptionWireGuardGSO, OptionTUNGSO, + OptionLegacyDNSTransport, + OptionLegacyDNSFakeIPOptions, + OptionOutboundDNSRuleItem, + OptionMissingDomainResolver, } diff --git a/option/outbound.go b/option/outbound.go index 5cadd3e2..99e3361a 100644 --- a/option/outbound.go +++ b/option/outbound.go @@ -77,12 +77,46 @@ type DialerOptions struct { TCPMultiPath bool `json:"tcp_multi_path,omitempty"` UDPFragment *bool `json:"udp_fragment,omitempty"` UDPFragmentDefault bool `json:"-"` - DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"` + DomainResolver *DomainResolveOptions `json:"domain_resolver,omitempty"` NetworkStrategy *NetworkStrategy `json:"network_strategy,omitempty"` NetworkType badoption.Listable[InterfaceType] `json:"network_type,omitempty"` FallbackNetworkType badoption.Listable[InterfaceType] `json:"fallback_network_type,omitempty"` FallbackDelay badoption.Duration `json:"fallback_delay,omitempty"` IsWireGuardListener bool `json:"-"` + + // Deprecated: migrated to domain resolver + DomainStrategy DomainStrategy `json:"domain_strategy,omitempty"` +} + +type _DomainResolveOptions struct { + Server string `json:"server"` + Strategy DomainStrategy `json:"strategy,omitempty"` + DisableCache bool `json:"disable_cache,omitempty"` + RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"` + ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"` +} + +type DomainResolveOptions _DomainResolveOptions + +func (o DomainResolveOptions) MarshalJSON() ([]byte, error) { + if o.Strategy == DomainStrategy(C.DomainStrategyAsIS) && + !o.DisableCache && + o.RewriteTTL == nil && + o.ClientSubnet == nil { + return json.Marshal(o.Server) + } else { + return json.Marshal((_DomainResolveOptions)(o)) + } +} + +func (o *DomainResolveOptions) UnmarshalJSON(bytes []byte) error { + var stringValue string + err := json.Unmarshal(bytes, &stringValue) + if err == nil { + o.Server = stringValue + return nil + } + return json.Unmarshal(bytes, (*_DomainResolveOptions)(o)) } func (o *DialerOptions) TakeDialerOptions() DialerOptions { @@ -107,6 +141,10 @@ func (o ServerOptions) Build() M.Socksaddr { return M.ParseSocksaddrHostPort(o.Server, o.ServerPort) } +func (o ServerOptions) ServerIsDomain() bool { + return M.IsDomainName(o.Server) +} + func (o *ServerOptions) TakeServerOptions() ServerOptions { return *o } diff --git a/option/route.go b/option/route.go index 1eb2294b..72c27e24 100644 --- a/option/route.go +++ b/option/route.go @@ -13,6 +13,7 @@ type RouteOptions struct { OverrideAndroidVPN bool `json:"override_android_vpn,omitempty"` DefaultInterface string `json:"default_interface,omitempty"` DefaultMark FwMark `json:"default_mark,omitempty"` + DomainResolver *DomainResolveOptions `json:"domain_resolver,omitempty"` DefaultNetworkStrategy *NetworkStrategy `json:"default_network_strategy,omitempty"` DefaultNetworkType badoption.Listable[InterfaceType] `json:"default_network_type,omitempty"` DefaultFallbackNetworkType badoption.Listable[InterfaceType] `json:"default_fallback_network_type,omitempty"` diff --git a/protocol/direct/outbound.go b/protocol/direct/outbound.go index d173ec53..9b454f58 100644 --- a/protocol/direct/outbound.go +++ b/protocol/direct/outbound.go @@ -42,16 +42,20 @@ type Outbound struct { func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.DirectOutboundOptions) (adapter.Outbound, error) { options.UDPFragmentDefault = true - outboundDialer, err := dialer.NewDirect(ctx, options.DialerOptions) + if options.Detour != "" { + return nil, E.New("`detour` is not supported in direct context") + } + outboundDialer, err := dialer.New(ctx, options.DialerOptions, false) if err != nil { return nil, err } outbound := &Outbound{ - Adapter: outbound.NewAdapterWithDialerOptions(C.TypeDirect, tag, []string{N.NetworkTCP, N.NetworkUDP}, options.DialerOptions), - logger: logger, + Adapter: outbound.NewAdapterWithDialerOptions(C.TypeDirect, tag, []string{N.NetworkTCP, N.NetworkUDP}, options.DialerOptions), + logger: logger, + //nolint:staticcheck domainStrategy: C.DomainStrategy(options.DomainStrategy), fallbackDelay: time.Duration(options.FallbackDelay), - dialer: outboundDialer, + dialer: outboundDialer.(dialer.ParallelInterfaceDialer), // loopBack: newLoopBackDetector(router), } //nolint:staticcheck diff --git a/protocol/http/outbound.go b/protocol/http/outbound.go index c58f3071..0570dde5 100644 --- a/protocol/http/outbound.go +++ b/protocol/http/outbound.go @@ -30,7 +30,7 @@ type Outbound struct { } func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.HTTPOutboundOptions) (adapter.Outbound, error) { - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/hysteria/outbound.go b/protocol/hysteria/outbound.go index e1d8716c..7746df13 100644 --- a/protocol/hysteria/outbound.go +++ b/protocol/hysteria/outbound.go @@ -47,7 +47,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL if err != nil { return nil, err } - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/hysteria2/outbound.go b/protocol/hysteria2/outbound.go index 74e87b37..c805f07e 100644 --- a/protocol/hysteria2/outbound.go +++ b/protocol/hysteria2/outbound.go @@ -60,7 +60,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL return nil, E.New("unknown obfs type: ", options.Obfs.Type) } } - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/shadowsocks/outbound.go b/protocol/shadowsocks/outbound.go index 7e7277ef..875c9e69 100644 --- a/protocol/shadowsocks/outbound.go +++ b/protocol/shadowsocks/outbound.go @@ -44,7 +44,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL if err != nil { return nil, err } - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/shadowtls/inbound.go b/protocol/shadowtls/inbound.go index 5ae5656f..1db191d8 100644 --- a/protocol/shadowtls/inbound.go +++ b/protocol/shadowtls/inbound.go @@ -47,7 +47,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo if options.Version > 1 { handshakeForServerName = make(map[string]shadowtls.HandshakeConfig) for serverName, serverOptions := range options.HandshakeForServerName { - handshakeDialer, err := dialer.New(ctx, serverOptions.DialerOptions) + handshakeDialer, err := dialer.New(ctx, serverOptions.DialerOptions, serverOptions.ServerIsDomain()) if err != nil { return nil, err } @@ -57,7 +57,7 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo } } } - handshakeDialer, err := dialer.New(ctx, options.Handshake.DialerOptions) + handshakeDialer, err := dialer.New(ctx, options.Handshake.DialerOptions, options.Handshake.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/shadowtls/outbound.go b/protocol/shadowtls/outbound.go index 2b480729..0731b033 100644 --- a/protocol/shadowtls/outbound.go +++ b/protocol/shadowtls/outbound.go @@ -68,7 +68,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL tlsHandshakeFunc = shadowtls.DefaultTLSHandshakeFunc(options.Password, stdTLSConfig) } } - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/socks/outbound.go b/protocol/socks/outbound.go index 323149e2..851412ff 100644 --- a/protocol/socks/outbound.go +++ b/protocol/socks/outbound.go @@ -46,7 +46,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL if err != nil { return nil, err } - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/ssh/outbound.go b/protocol/ssh/outbound.go index eb9970b5..304ea389 100644 --- a/protocol/ssh/outbound.go +++ b/protocol/ssh/outbound.go @@ -49,7 +49,7 @@ type Outbound struct { } func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SSHOutboundOptions) (adapter.Outbound, error) { - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/tor/outbound.go b/protocol/tor/outbound.go index 58824b53..9a0e2d65 100644 --- a/protocol/tor/outbound.go +++ b/protocol/tor/outbound.go @@ -75,7 +75,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL } startConf.TorrcFile = torrcFile } - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, false) if err != nil { return nil, err } diff --git a/protocol/trojan/outbound.go b/protocol/trojan/outbound.go index 82889bc1..37a6933c 100644 --- a/protocol/trojan/outbound.go +++ b/protocol/trojan/outbound.go @@ -38,7 +38,7 @@ type Outbound struct { } func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TrojanOutboundOptions) (adapter.Outbound, error) { - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/tuic/outbound.go b/protocol/tuic/outbound.go index 49b01f96..a31d4850 100644 --- a/protocol/tuic/outbound.go +++ b/protocol/tuic/outbound.go @@ -60,7 +60,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL case "quic": tuicUDPStream = true } - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/vless/outbound.go b/protocol/vless/outbound.go index 1d832a65..e0208be9 100644 --- a/protocol/vless/outbound.go +++ b/protocol/vless/outbound.go @@ -41,7 +41,7 @@ type Outbound struct { } func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VLESSOutboundOptions) (adapter.Outbound, error) { - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/vmess/outbound.go b/protocol/vmess/outbound.go index d41b30d9..be05990e 100644 --- a/protocol/vmess/outbound.go +++ b/protocol/vmess/outbound.go @@ -41,7 +41,7 @@ type Outbound struct { } func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.VMessOutboundOptions) (adapter.Outbound, error) { - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) if err != nil { return nil, err } diff --git a/protocol/wireguard/endpoint.go b/protocol/wireguard/endpoint.go index 300701a9..0485d63b 100644 --- a/protocol/wireguard/endpoint.go +++ b/protocol/wireguard/endpoint.go @@ -53,7 +53,7 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL if options.Detour == "" { options.IsWireGuardListener = true } - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, false) if err != nil { return nil, err } @@ -81,9 +81,7 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL PrivateKey: options.PrivateKey, ListenPort: options.ListenPort, ResolvePeer: func(domain string) (netip.Addr, error) { - endpointAddresses, lookupErr := ep.dnsRouter.Lookup(ctx, domain, adapter.DNSQueryOptions{ - Strategy: C.DomainStrategy(options.DomainStrategy), - }) + endpointAddresses, lookupErr := ep.dnsRouter.Lookup(ctx, domain, outboundDialer.(dialer.ResolveDialer).QueryOptions()) if lookupErr != nil { return netip.Addr{}, lookupErr } diff --git a/protocol/wireguard/outbound.go b/protocol/wireguard/outbound.go index 4aa49a8d..d4eea10b 100644 --- a/protocol/wireguard/outbound.go +++ b/protocol/wireguard/outbound.go @@ -56,7 +56,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL } else if options.GSO { return nil, E.New("gso is conflict with detour") } - outboundDialer, err := dialer.New(ctx, options.DialerOptions) + outboundDialer, err := dialer.New(ctx, options.DialerOptions, options.ServerIsDomain()) if err != nil { return nil, err } @@ -94,9 +94,7 @@ func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextL Address: options.LocalAddress, PrivateKey: options.PrivateKey, ResolvePeer: func(domain string) (netip.Addr, error) { - endpointAddresses, lookupErr := outbound.dnsRouter.Lookup(ctx, domain, adapter.DNSQueryOptions{ - Strategy: C.DomainStrategy(options.DomainStrategy), - }) + endpointAddresses, lookupErr := outbound.dnsRouter.Lookup(ctx, domain, outboundDialer.(dialer.ResolveDialer).QueryOptions()) if lookupErr != nil { return netip.Addr{}, lookupErr } diff --git a/route/network.go b/route/network.go index 97d165f1..1bf0a7ca 100644 --- a/route/network.go +++ b/route/network.go @@ -4,6 +4,7 @@ import ( "context" "errors" "net" + "net/netip" "os" "runtime" "strings" @@ -55,13 +56,21 @@ type NetworkManager struct { } func NewNetworkManager(ctx context.Context, logger logger.ContextLogger, routeOptions option.RouteOptions) (*NetworkManager, error) { + defaultDomainResolver := common.PtrValueOrDefault(routeOptions.DomainResolver) nm := &NetworkManager{ logger: logger, interfaceFinder: control.NewDefaultInterfaceFinder(), autoDetectInterface: routeOptions.AutoDetectInterface, defaultOptions: adapter.NetworkOptions{ - BindInterface: routeOptions.DefaultInterface, - RoutingMark: uint32(routeOptions.DefaultMark), + BindInterface: routeOptions.DefaultInterface, + RoutingMark: uint32(routeOptions.DefaultMark), + DomainResolver: defaultDomainResolver.Server, + DomainResolveOptions: adapter.DNSQueryOptions{ + Strategy: C.DomainStrategy(defaultDomainResolver.Strategy), + DisableCache: defaultDomainResolver.DisableCache, + RewriteTTL: defaultDomainResolver.RewriteTTL, + ClientSubnet: defaultDomainResolver.ClientSubnet.Build(netip.Prefix{}), + }, NetworkStrategy: (*C.NetworkStrategy)(routeOptions.DefaultNetworkStrategy), NetworkType: common.Map(routeOptions.DefaultNetworkType, option.InterfaceType.Build), FallbackNetworkType: common.Map(routeOptions.DefaultFallbackNetworkType, option.InterfaceType.Build), diff --git a/route/rule/rule_action.go b/route/rule/rule_action.go index 98e47f03..6730ed76 100644 --- a/route/rule/rule_action.go +++ b/route/rule/rule_action.go @@ -49,7 +49,7 @@ func NewRuleAction(ctx context.Context, logger logger.ContextLogger, action opti UDPTimeout: time.Duration(action.RouteOptionsOptions.UDPTimeout), }, nil case C.RuleActionTypeDirect: - directDialer, err := dialer.New(ctx, option.DialerOptions(action.DirectOptions)) + directDialer, err := dialer.New(ctx, option.DialerOptions(action.DirectOptions), false) if err != nil { return nil, err } diff --git a/route/rule/rule_dns.go b/route/rule/rule_dns.go index 9d1c69b8..087fb7b2 100644 --- a/route/rule/rule_dns.go +++ b/route/rule/rule_dns.go @@ -210,7 +210,7 @@ func NewDefaultDNSRule(ctx context.Context, logger log.ContextLogger, options op rule.allItems = append(rule.allItems, item) } if len(options.Outbound) > 0 { - item := NewOutboundRule(options.Outbound) + item := NewOutboundRule(ctx, options.Outbound) rule.items = append(rule.items, item) rule.allItems = append(rule.allItems, item) } diff --git a/route/rule/rule_item_outbound.go b/route/rule/rule_item_outbound.go index 3f37dee7..a13d0597 100644 --- a/route/rule/rule_item_outbound.go +++ b/route/rule/rule_item_outbound.go @@ -1,9 +1,11 @@ package rule import ( + "context" "strings" "github.com/sagernet/sing-box/adapter" + "github.com/sagernet/sing-box/experimental/deprecated" F "github.com/sagernet/sing/common/format" ) @@ -15,7 +17,8 @@ type OutboundItem struct { matchAny bool } -func NewOutboundRule(outbounds []string) *OutboundItem { +func NewOutboundRule(ctx context.Context, outbounds []string) *OutboundItem { + deprecated.Report(ctx, deprecated.OptionOutboundDNSRuleItem) rule := &OutboundItem{outbounds: outbounds, outboundMap: make(map[string]bool)} for _, outbound := range outbounds { if outbound == "any" { @@ -28,8 +31,8 @@ func NewOutboundRule(outbounds []string) *OutboundItem { } func (r *OutboundItem) Match(metadata *adapter.InboundContext) bool { - if r.matchAny && metadata.Outbound != "" { - return true + if r.matchAny { + return metadata.Outbound != "" } return r.outboundMap[metadata.Outbound] }