mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-04-03 20:07:36 +03:00
refactor: DNS
This commit is contained in:
parent
572ee775b1
commit
6c9e61a0a0
89 changed files with 4794 additions and 1733 deletions
414
route/router.go
414
route/router.go
|
@ -2,17 +2,10 @@ package route
|
|||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"net/url"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing-box/adapter"
|
||||
"github.com/sagernet/sing-box/common/dialer"
|
||||
"github.com/sagernet/sing-box/common/geoip"
|
||||
"github.com/sagernet/sing-box/common/geosite"
|
||||
"github.com/sagernet/sing-box/common/process"
|
||||
"github.com/sagernet/sing-box/common/taskmonitor"
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
|
@ -20,13 +13,7 @@ import (
|
|||
"github.com/sagernet/sing-box/log"
|
||||
"github.com/sagernet/sing-box/option"
|
||||
R "github.com/sagernet/sing-box/route/rule"
|
||||
"github.com/sagernet/sing-box/transport/fakeip"
|
||||
"github.com/sagernet/sing-dns"
|
||||
"github.com/sagernet/sing/common"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
F "github.com/sagernet/sing/common/format"
|
||||
M "github.com/sagernet/sing/common/metadata"
|
||||
N "github.com/sagernet/sing/common/network"
|
||||
"github.com/sagernet/sing/common/task"
|
||||
"github.com/sagernet/sing/service"
|
||||
"github.com/sagernet/sing/service/pause"
|
||||
|
@ -35,334 +22,71 @@ import (
|
|||
var _ adapter.Router = (*Router)(nil)
|
||||
|
||||
type Router struct {
|
||||
ctx context.Context
|
||||
logger log.ContextLogger
|
||||
dnsLogger log.ContextLogger
|
||||
inbound adapter.InboundManager
|
||||
outbound adapter.OutboundManager
|
||||
connection adapter.ConnectionManager
|
||||
network adapter.NetworkManager
|
||||
rules []adapter.Rule
|
||||
needGeoIPDatabase bool
|
||||
needGeositeDatabase bool
|
||||
geoIPOptions option.GeoIPOptions
|
||||
geositeOptions option.GeositeOptions
|
||||
geoIPReader *geoip.Reader
|
||||
geositeReader *geosite.Reader
|
||||
geositeCache map[string]adapter.Rule
|
||||
needFindProcess bool
|
||||
dnsClient *dns.Client
|
||||
defaultDomainStrategy dns.DomainStrategy
|
||||
dnsRules []adapter.DNSRule
|
||||
ruleSets []adapter.RuleSet
|
||||
ruleSetMap map[string]adapter.RuleSet
|
||||
defaultTransport dns.Transport
|
||||
transports []dns.Transport
|
||||
transportMap map[string]dns.Transport
|
||||
transportDomainStrategy map[dns.Transport]dns.DomainStrategy
|
||||
dnsReverseMapping *DNSReverseMapping
|
||||
fakeIPStore adapter.FakeIPStore
|
||||
processSearcher process.Searcher
|
||||
pauseManager pause.Manager
|
||||
tracker adapter.ConnectionTracker
|
||||
platformInterface platform.Interface
|
||||
needWIFIState bool
|
||||
started bool
|
||||
ctx context.Context
|
||||
logger log.ContextLogger
|
||||
inbound adapter.InboundManager
|
||||
outbound adapter.OutboundManager
|
||||
dns adapter.DNSRouter
|
||||
dnsTransport adapter.DNSTransportManager
|
||||
connection adapter.ConnectionManager
|
||||
network adapter.NetworkManager
|
||||
rules []adapter.Rule
|
||||
needFindProcess bool
|
||||
ruleSets []adapter.RuleSet
|
||||
ruleSetMap map[string]adapter.RuleSet
|
||||
processSearcher process.Searcher
|
||||
pauseManager pause.Manager
|
||||
tracker adapter.ConnectionTracker
|
||||
platformInterface platform.Interface
|
||||
needWIFIState bool
|
||||
started bool
|
||||
}
|
||||
|
||||
func NewRouter(ctx context.Context, logFactory log.Factory, options option.RouteOptions, dnsOptions option.DNSOptions) (*Router, error) {
|
||||
router := &Router{
|
||||
ctx: ctx,
|
||||
logger: logFactory.NewLogger("router"),
|
||||
dnsLogger: logFactory.NewLogger("dns"),
|
||||
inbound: service.FromContext[adapter.InboundManager](ctx),
|
||||
outbound: service.FromContext[adapter.OutboundManager](ctx),
|
||||
connection: service.FromContext[adapter.ConnectionManager](ctx),
|
||||
network: service.FromContext[adapter.NetworkManager](ctx),
|
||||
rules: make([]adapter.Rule, 0, len(options.Rules)),
|
||||
dnsRules: make([]adapter.DNSRule, 0, len(dnsOptions.Rules)),
|
||||
ruleSetMap: make(map[string]adapter.RuleSet),
|
||||
needGeoIPDatabase: hasRule(options.Rules, isGeoIPRule) || hasDNSRule(dnsOptions.Rules, isGeoIPDNSRule),
|
||||
needGeositeDatabase: hasRule(options.Rules, isGeositeRule) || hasDNSRule(dnsOptions.Rules, isGeositeDNSRule),
|
||||
geoIPOptions: common.PtrValueOrDefault(options.GeoIP),
|
||||
geositeOptions: common.PtrValueOrDefault(options.Geosite),
|
||||
geositeCache: make(map[string]adapter.Rule),
|
||||
needFindProcess: hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess,
|
||||
defaultDomainStrategy: dns.DomainStrategy(dnsOptions.Strategy),
|
||||
pauseManager: service.FromContext[pause.Manager](ctx),
|
||||
platformInterface: service.FromContext[platform.Interface](ctx),
|
||||
needWIFIState: hasRule(options.Rules, isWIFIRule) || hasDNSRule(dnsOptions.Rules, isWIFIDNSRule),
|
||||
}
|
||||
service.MustRegister[adapter.Router](ctx, router)
|
||||
router.dnsClient = dns.NewClient(dns.ClientOptions{
|
||||
DisableCache: dnsOptions.DNSClientOptions.DisableCache,
|
||||
DisableExpire: dnsOptions.DNSClientOptions.DisableExpire,
|
||||
IndependentCache: dnsOptions.DNSClientOptions.IndependentCache,
|
||||
CacheCapacity: dnsOptions.DNSClientOptions.CacheCapacity,
|
||||
RDRC: func() dns.RDRCStore {
|
||||
cacheFile := service.FromContext[adapter.CacheFile](ctx)
|
||||
if cacheFile == nil {
|
||||
return nil
|
||||
}
|
||||
if !cacheFile.StoreRDRC() {
|
||||
return nil
|
||||
}
|
||||
return cacheFile
|
||||
},
|
||||
Logger: router.dnsLogger,
|
||||
})
|
||||
for i, ruleOptions := range options.Rules {
|
||||
routeRule, err := R.NewRule(ctx, router.logger, ruleOptions, true)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse rule[", i, "]")
|
||||
}
|
||||
router.rules = append(router.rules, routeRule)
|
||||
}
|
||||
for i, dnsRuleOptions := range dnsOptions.Rules {
|
||||
dnsRule, err := R.NewDNSRule(ctx, router.logger, dnsRuleOptions, true)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse dns rule[", i, "]")
|
||||
}
|
||||
router.dnsRules = append(router.dnsRules, dnsRule)
|
||||
}
|
||||
for i, ruleSetOptions := range options.RuleSet {
|
||||
if _, exists := router.ruleSetMap[ruleSetOptions.Tag]; exists {
|
||||
return nil, E.New("duplicate rule-set tag: ", ruleSetOptions.Tag)
|
||||
}
|
||||
ruleSet, err := R.NewRuleSet(ctx, router.logger, ruleSetOptions)
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse rule-set[", i, "]")
|
||||
}
|
||||
router.ruleSets = append(router.ruleSets, ruleSet)
|
||||
router.ruleSetMap[ruleSetOptions.Tag] = ruleSet
|
||||
func NewRouter(ctx context.Context, logFactory log.Factory, options option.RouteOptions, dnsOptions option.DNSOptions) *Router {
|
||||
return &Router{
|
||||
ctx: ctx,
|
||||
logger: logFactory.NewLogger("router"),
|
||||
inbound: service.FromContext[adapter.InboundManager](ctx),
|
||||
outbound: service.FromContext[adapter.OutboundManager](ctx),
|
||||
dns: service.FromContext[adapter.DNSRouter](ctx),
|
||||
dnsTransport: service.FromContext[adapter.DNSTransportManager](ctx),
|
||||
connection: service.FromContext[adapter.ConnectionManager](ctx),
|
||||
network: service.FromContext[adapter.NetworkManager](ctx),
|
||||
rules: make([]adapter.Rule, 0, len(options.Rules)),
|
||||
ruleSetMap: make(map[string]adapter.RuleSet),
|
||||
needFindProcess: hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess,
|
||||
pauseManager: service.FromContext[pause.Manager](ctx),
|
||||
platformInterface: service.FromContext[platform.Interface](ctx),
|
||||
needWIFIState: hasRule(options.Rules, isWIFIRule) || hasDNSRule(dnsOptions.Rules, isWIFIDNSRule),
|
||||
}
|
||||
}
|
||||
|
||||
transports := make([]dns.Transport, len(dnsOptions.Servers))
|
||||
dummyTransportMap := make(map[string]dns.Transport)
|
||||
transportMap := make(map[string]dns.Transport)
|
||||
transportTags := make([]string, len(dnsOptions.Servers))
|
||||
transportTagMap := make(map[string]bool)
|
||||
transportDomainStrategy := make(map[dns.Transport]dns.DomainStrategy)
|
||||
for i, server := range dnsOptions.Servers {
|
||||
var tag string
|
||||
if server.Tag != "" {
|
||||
tag = server.Tag
|
||||
} else {
|
||||
tag = F.ToString(i)
|
||||
func (r *Router) Initialize(rules []option.Rule, ruleSets []option.RuleSet) error {
|
||||
for i, options := range rules {
|
||||
rule, err := R.NewRule(r.ctx, r.logger, options, false)
|
||||
if err != nil {
|
||||
return E.Cause(err, "parse rule[", i, "]")
|
||||
}
|
||||
if transportTagMap[tag] {
|
||||
return nil, E.New("duplicate dns server tag: ", tag)
|
||||
}
|
||||
transportTags[i] = tag
|
||||
transportTagMap[tag] = true
|
||||
r.rules = append(r.rules, rule)
|
||||
}
|
||||
outboundManager := service.FromContext[adapter.OutboundManager](ctx)
|
||||
for {
|
||||
lastLen := len(dummyTransportMap)
|
||||
for i, server := range dnsOptions.Servers {
|
||||
tag := transportTags[i]
|
||||
if _, exists := dummyTransportMap[tag]; exists {
|
||||
continue
|
||||
}
|
||||
var detour N.Dialer
|
||||
if server.Detour == "" {
|
||||
detour = dialer.NewDefaultOutbound(outboundManager)
|
||||
} else {
|
||||
detour = dialer.NewDetour(outboundManager, server.Detour)
|
||||
}
|
||||
var serverProtocol string
|
||||
switch server.Address {
|
||||
case "local":
|
||||
serverProtocol = "local"
|
||||
default:
|
||||
serverURL, _ := url.Parse(server.Address)
|
||||
var serverAddress string
|
||||
if serverURL != nil {
|
||||
if serverURL.Scheme == "" {
|
||||
serverProtocol = "udp"
|
||||
} else {
|
||||
serverProtocol = serverURL.Scheme
|
||||
}
|
||||
serverAddress = serverURL.Hostname()
|
||||
}
|
||||
if serverAddress == "" {
|
||||
serverAddress = server.Address
|
||||
}
|
||||
notIpAddress := !M.ParseSocksaddr(serverAddress).Addr.IsValid()
|
||||
if server.AddressResolver != "" {
|
||||
if !transportTagMap[server.AddressResolver] {
|
||||
return nil, E.New("parse dns server[", tag, "]: address resolver not found: ", server.AddressResolver)
|
||||
}
|
||||
if upstream, exists := dummyTransportMap[server.AddressResolver]; exists {
|
||||
detour = dns.NewDialerWrapper(detour, router.dnsClient, upstream, dns.DomainStrategy(server.AddressStrategy), time.Duration(server.AddressFallbackDelay))
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
} else if notIpAddress && strings.Contains(server.Address, ".") {
|
||||
return nil, E.New("parse dns server[", tag, "]: missing address_resolver")
|
||||
}
|
||||
}
|
||||
var clientSubnet netip.Prefix
|
||||
if server.ClientSubnet != nil {
|
||||
clientSubnet = netip.Prefix(common.PtrValueOrDefault(server.ClientSubnet))
|
||||
} else if dnsOptions.ClientSubnet != nil {
|
||||
clientSubnet = netip.Prefix(common.PtrValueOrDefault(dnsOptions.ClientSubnet))
|
||||
}
|
||||
if serverProtocol == "" {
|
||||
serverProtocol = "transport"
|
||||
}
|
||||
transport, err := dns.CreateTransport(dns.TransportOptions{
|
||||
Context: ctx,
|
||||
Logger: logFactory.NewLogger(F.ToString("dns/", serverProtocol, "[", tag, "]")),
|
||||
Name: tag,
|
||||
Dialer: detour,
|
||||
Address: server.Address,
|
||||
ClientSubnet: clientSubnet,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, E.Cause(err, "parse dns server[", tag, "]")
|
||||
}
|
||||
transports[i] = transport
|
||||
dummyTransportMap[tag] = transport
|
||||
if server.Tag != "" {
|
||||
transportMap[server.Tag] = transport
|
||||
}
|
||||
strategy := dns.DomainStrategy(server.Strategy)
|
||||
if strategy != dns.DomainStrategyAsIS {
|
||||
transportDomainStrategy[transport] = strategy
|
||||
}
|
||||
for i, options := range ruleSets {
|
||||
if _, exists := r.ruleSetMap[options.Tag]; exists {
|
||||
return E.New("duplicate rule-set tag: ", options.Tag)
|
||||
}
|
||||
if len(transports) == len(dummyTransportMap) {
|
||||
break
|
||||
ruleSet, err := R.NewRuleSet(r.ctx, r.logger, options)
|
||||
if err != nil {
|
||||
return E.Cause(err, "parse rule-set[", i, "]")
|
||||
}
|
||||
if lastLen != len(dummyTransportMap) {
|
||||
continue
|
||||
}
|
||||
unresolvedTags := common.MapIndexed(common.FilterIndexed(dnsOptions.Servers, func(index int, server option.DNSServerOptions) bool {
|
||||
_, exists := dummyTransportMap[transportTags[index]]
|
||||
return !exists
|
||||
}), func(index int, server option.DNSServerOptions) string {
|
||||
return transportTags[index]
|
||||
})
|
||||
if len(unresolvedTags) == 0 {
|
||||
panic(F.ToString("unexpected unresolved dns servers: ", len(transports), " ", len(dummyTransportMap), " ", len(transportMap)))
|
||||
}
|
||||
return nil, E.New("found circular reference in dns servers: ", strings.Join(unresolvedTags, " "))
|
||||
r.ruleSets = append(r.ruleSets, ruleSet)
|
||||
r.ruleSetMap[options.Tag] = ruleSet
|
||||
}
|
||||
var defaultTransport dns.Transport
|
||||
if dnsOptions.Final != "" {
|
||||
defaultTransport = dummyTransportMap[dnsOptions.Final]
|
||||
if defaultTransport == nil {
|
||||
return nil, E.New("default dns server not found: ", dnsOptions.Final)
|
||||
}
|
||||
}
|
||||
if defaultTransport == nil {
|
||||
if len(transports) == 0 {
|
||||
transports = append(transports, common.Must1(dns.CreateTransport(dns.TransportOptions{
|
||||
Context: ctx,
|
||||
Name: "local",
|
||||
Address: "local",
|
||||
Dialer: common.Must1(dialer.NewDefault(ctx, option.DialerOptions{})),
|
||||
})))
|
||||
}
|
||||
defaultTransport = transports[0]
|
||||
}
|
||||
if _, isFakeIP := defaultTransport.(adapter.FakeIPTransport); isFakeIP {
|
||||
return nil, E.New("default DNS server cannot be fakeip")
|
||||
}
|
||||
router.defaultTransport = defaultTransport
|
||||
router.transports = transports
|
||||
router.transportMap = transportMap
|
||||
router.transportDomainStrategy = transportDomainStrategy
|
||||
|
||||
if dnsOptions.ReverseMapping {
|
||||
router.dnsReverseMapping = NewDNSReverseMapping()
|
||||
}
|
||||
|
||||
if fakeIPOptions := dnsOptions.FakeIP; fakeIPOptions != nil && dnsOptions.FakeIP.Enabled {
|
||||
var inet4Range netip.Prefix
|
||||
var inet6Range netip.Prefix
|
||||
if fakeIPOptions.Inet4Range != nil {
|
||||
inet4Range = *fakeIPOptions.Inet4Range
|
||||
}
|
||||
if fakeIPOptions.Inet6Range != nil {
|
||||
inet6Range = *fakeIPOptions.Inet6Range
|
||||
}
|
||||
router.fakeIPStore = fakeip.NewStore(ctx, router.logger, inet4Range, inet6Range)
|
||||
}
|
||||
return router, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Router) Start(stage adapter.StartStage) error {
|
||||
monitor := taskmonitor.New(r.logger, C.StartTimeout)
|
||||
switch stage {
|
||||
case adapter.StartStateInitialize:
|
||||
if r.fakeIPStore != nil {
|
||||
monitor.Start("initialize fakeip store")
|
||||
err := r.fakeIPStore.Start()
|
||||
monitor.Finish()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case adapter.StartStateStart:
|
||||
if r.needGeoIPDatabase {
|
||||
monitor.Start("initialize geoip database")
|
||||
err := r.prepareGeoIPDatabase()
|
||||
monitor.Finish()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if r.needGeositeDatabase {
|
||||
monitor.Start("initialize geosite database")
|
||||
err := r.prepareGeositeDatabase()
|
||||
monitor.Finish()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if r.needGeositeDatabase {
|
||||
for _, rule := range r.rules {
|
||||
err := rule.UpdateGeosite()
|
||||
if err != nil {
|
||||
r.logger.Error("failed to initialize geosite: ", err)
|
||||
}
|
||||
}
|
||||
for _, rule := range r.dnsRules {
|
||||
err := rule.UpdateGeosite()
|
||||
if err != nil {
|
||||
r.logger.Error("failed to initialize geosite: ", err)
|
||||
}
|
||||
}
|
||||
err := common.Close(r.geositeReader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.geositeCache = nil
|
||||
r.geositeReader = nil
|
||||
}
|
||||
|
||||
monitor.Start("initialize DNS client")
|
||||
r.dnsClient.Start()
|
||||
monitor.Finish()
|
||||
|
||||
for i, rule := range r.dnsRules {
|
||||
monitor.Start("initialize DNS rule[", i, "]")
|
||||
err := rule.Start()
|
||||
monitor.Finish()
|
||||
if err != nil {
|
||||
return E.Cause(err, "initialize DNS rule[", i, "]")
|
||||
}
|
||||
}
|
||||
for i, transport := range r.transports {
|
||||
monitor.Start("initialize DNS transport[", i, "]")
|
||||
err := transport.Start()
|
||||
monitor.Finish()
|
||||
if err != nil {
|
||||
return E.Cause(err, "initialize DNS server[", i, "]")
|
||||
}
|
||||
}
|
||||
var cacheContext *adapter.HTTPStartContext
|
||||
if len(r.ruleSets) > 0 {
|
||||
monitor.Start("initialize rule-set")
|
||||
|
@ -438,7 +162,7 @@ func (r *Router) Start(stage adapter.StartStage) error {
|
|||
r.started = true
|
||||
return nil
|
||||
case adapter.StartStateStarted:
|
||||
for _, ruleSet := range r.ruleSetMap {
|
||||
for _, ruleSet := range r.ruleSets {
|
||||
ruleSet.Cleanup()
|
||||
}
|
||||
runtime.GC()
|
||||
|
@ -456,34 +180,6 @@ func (r *Router) Close() error {
|
|||
})
|
||||
monitor.Finish()
|
||||
}
|
||||
for i, rule := range r.dnsRules {
|
||||
monitor.Start("close dns rule[", i, "]")
|
||||
err = E.Append(err, rule.Close(), func(err error) error {
|
||||
return E.Cause(err, "close dns rule[", i, "]")
|
||||
})
|
||||
monitor.Finish()
|
||||
}
|
||||
for i, transport := range r.transports {
|
||||
monitor.Start("close dns transport[", i, "]")
|
||||
err = E.Append(err, transport.Close(), func(err error) error {
|
||||
return E.Cause(err, "close dns transport[", i, "]")
|
||||
})
|
||||
monitor.Finish()
|
||||
}
|
||||
if r.geoIPReader != nil {
|
||||
monitor.Start("close geoip reader")
|
||||
err = E.Append(err, r.geoIPReader.Close(), func(err error) error {
|
||||
return E.Cause(err, "close geoip reader")
|
||||
})
|
||||
monitor.Finish()
|
||||
}
|
||||
if r.fakeIPStore != nil {
|
||||
monitor.Start("close fakeip store")
|
||||
err = E.Append(err, r.fakeIPStore.Close(), func(err error) error {
|
||||
return E.Cause(err, "close fakeip store")
|
||||
})
|
||||
monitor.Finish()
|
||||
}
|
||||
for i, ruleSet := range r.ruleSets {
|
||||
monitor.Start("close rule-set[", i, "]")
|
||||
err = E.Append(err, ruleSet.Close(), func(err error) error {
|
||||
|
@ -494,10 +190,6 @@ func (r *Router) Close() error {
|
|||
return err
|
||||
}
|
||||
|
||||
func (r *Router) FakeIPStore() adapter.FakeIPStore {
|
||||
return r.fakeIPStore
|
||||
}
|
||||
|
||||
func (r *Router) RuleSet(tag string) (adapter.RuleSet, bool) {
|
||||
ruleSet, loaded := r.ruleSetMap[tag]
|
||||
return ruleSet, loaded
|
||||
|
@ -517,7 +209,5 @@ func (r *Router) SetTracker(tracker adapter.ConnectionTracker) {
|
|||
|
||||
func (r *Router) ResetNetwork() {
|
||||
r.network.ResetNetwork()
|
||||
for _, transport := range r.transports {
|
||||
transport.Reset()
|
||||
}
|
||||
r.dns.ResetNetwork()
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue