mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-11 17:47:20 +10:00
285 lines
8.6 KiB
Go
285 lines
8.6 KiB
Go
package route
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"runtime"
|
|
"time"
|
|
|
|
"github.com/sagernet/sing-box/adapter"
|
|
"github.com/sagernet/sing-box/common/process"
|
|
"github.com/sagernet/sing-box/common/taskmonitor"
|
|
C "github.com/sagernet/sing-box/constant"
|
|
"github.com/sagernet/sing-box/log"
|
|
"github.com/sagernet/sing-box/option"
|
|
R "github.com/sagernet/sing-box/route/rule"
|
|
"github.com/sagernet/sing/common"
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
"github.com/sagernet/sing/common/task"
|
|
"github.com/sagernet/sing/contrab/freelru"
|
|
"github.com/sagernet/sing/contrab/maphash"
|
|
"github.com/sagernet/sing/service"
|
|
"github.com/sagernet/sing/service/pause"
|
|
)
|
|
|
|
var _ adapter.Router = (*Router)(nil)
|
|
|
|
type Router struct {
|
|
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
|
|
needFindNeighbor bool
|
|
leaseFiles []string
|
|
ruleSets []adapter.RuleSet
|
|
ruleSetMap map[string]adapter.RuleSet
|
|
processSearcher process.Searcher
|
|
processCache freelru.Cache[processCacheKey, processCacheEntry]
|
|
neighborResolver adapter.NeighborResolver
|
|
pauseManager pause.Manager
|
|
trackers []adapter.ConnectionTracker
|
|
platformInterface adapter.PlatformInterface
|
|
started bool
|
|
}
|
|
|
|
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,
|
|
needFindNeighbor: hasRule(options.Rules, isNeighborRule) || hasDNSRule(dnsOptions.Rules, isNeighborDNSRule) || options.FindNeighbor,
|
|
leaseFiles: options.DHCPLeaseFiles,
|
|
pauseManager: service.FromContext[pause.Manager](ctx),
|
|
platformInterface: service.FromContext[adapter.PlatformInterface](ctx),
|
|
}
|
|
}
|
|
|
|
func (r *Router) Initialize(rules []option.Rule, ruleSets []option.RuleSet) error {
|
|
for i, options := range rules {
|
|
err := R.ValidateNoNestedRuleActions(options)
|
|
if err != nil {
|
|
return E.Cause(err, "parse rule[", i, "]")
|
|
}
|
|
rule, err := R.NewRule(r.ctx, r.logger, options, false)
|
|
if err != nil {
|
|
return E.Cause(err, "parse rule[", i, "]")
|
|
}
|
|
r.rules = append(r.rules, rule)
|
|
}
|
|
for i, options := range ruleSets {
|
|
if _, exists := r.ruleSetMap[options.Tag]; exists {
|
|
return E.New("duplicate rule-set tag: ", options.Tag)
|
|
}
|
|
ruleSet, err := R.NewRuleSet(r.ctx, r.logger, options)
|
|
if err != nil {
|
|
return E.Cause(err, "parse rule-set[", i, "]")
|
|
}
|
|
r.ruleSets = append(r.ruleSets, ruleSet)
|
|
r.ruleSetMap[options.Tag] = ruleSet
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *Router) Start(stage adapter.StartStage) error {
|
|
monitor := taskmonitor.New(r.logger, C.StartTimeout)
|
|
switch stage {
|
|
case adapter.StartStateStart:
|
|
var cacheContext *adapter.HTTPStartContext
|
|
if len(r.ruleSets) > 0 {
|
|
monitor.Start("initialize rule-set")
|
|
cacheContext = adapter.NewHTTPStartContext(r.ctx)
|
|
var ruleSetStartGroup task.Group
|
|
for i, ruleSet := range r.ruleSets {
|
|
ruleSetInPlace := ruleSet
|
|
ruleSetStartGroup.Append0(func(ctx context.Context) error {
|
|
err := ruleSetInPlace.StartContext(ctx, cacheContext)
|
|
if err != nil {
|
|
return E.Cause(err, "initialize rule-set[", i, "]")
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
ruleSetStartGroup.Concurrency(5)
|
|
ruleSetStartGroup.FastFail()
|
|
err := ruleSetStartGroup.Run(r.ctx)
|
|
monitor.Finish()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if cacheContext != nil {
|
|
cacheContext.Close()
|
|
}
|
|
r.network.Initialize(r.ruleSets)
|
|
needFindProcess := r.needFindProcess
|
|
needFindNeighbor := r.needFindNeighbor
|
|
for _, ruleSet := range r.ruleSets {
|
|
metadata := ruleSet.Metadata()
|
|
if metadata.ContainsProcessRule {
|
|
needFindProcess = true
|
|
}
|
|
}
|
|
if C.IsAndroid && r.platformInterface != nil {
|
|
needFindProcess = true
|
|
}
|
|
r.needFindProcess = needFindProcess
|
|
if needFindProcess {
|
|
if r.platformInterface != nil && r.platformInterface.UsePlatformConnectionOwnerFinder() {
|
|
r.processSearcher = newPlatformSearcher(r.platformInterface)
|
|
} else {
|
|
monitor.Start("initialize process searcher")
|
|
searcher, err := process.NewSearcher(process.Config{
|
|
Logger: r.logger,
|
|
PackageManager: r.network.PackageManager(),
|
|
})
|
|
monitor.Finish()
|
|
if err != nil {
|
|
if err != os.ErrInvalid {
|
|
r.logger.Warn(E.Cause(err, "create process searcher"))
|
|
}
|
|
} else {
|
|
r.processSearcher = searcher
|
|
}
|
|
}
|
|
}
|
|
if r.processSearcher != nil {
|
|
processCache := common.Must1(freelru.NewSharded[processCacheKey, processCacheEntry](256, maphash.NewHasher[processCacheKey]().Hash32))
|
|
processCache.SetLifetime(200 * time.Millisecond)
|
|
r.processCache = processCache
|
|
}
|
|
r.needFindNeighbor = needFindNeighbor
|
|
if needFindNeighbor {
|
|
if r.platformInterface != nil && r.platformInterface.UsePlatformNeighborResolver() {
|
|
monitor.Start("initialize neighbor resolver")
|
|
resolver := newPlatformNeighborResolver(r.logger, r.platformInterface)
|
|
err := resolver.Start()
|
|
monitor.Finish()
|
|
if err != nil {
|
|
r.logger.Error(E.Cause(err, "start neighbor resolver"))
|
|
} else {
|
|
r.neighborResolver = resolver
|
|
}
|
|
} else {
|
|
monitor.Start("initialize neighbor resolver")
|
|
resolver, err := newNeighborResolver(r.logger, r.leaseFiles)
|
|
monitor.Finish()
|
|
if err != nil {
|
|
if err != os.ErrInvalid {
|
|
r.logger.Error(E.Cause(err, "create neighbor resolver"))
|
|
}
|
|
} else {
|
|
err = resolver.Start()
|
|
if err != nil {
|
|
r.logger.Error(E.Cause(err, "start neighbor resolver"))
|
|
} else {
|
|
r.neighborResolver = resolver
|
|
}
|
|
}
|
|
}
|
|
}
|
|
case adapter.StartStatePostStart:
|
|
for i, rule := range r.rules {
|
|
monitor.Start("initialize rule[", i, "]")
|
|
err := rule.Start()
|
|
monitor.Finish()
|
|
if err != nil {
|
|
return E.Cause(err, "initialize rule[", i, "]")
|
|
}
|
|
}
|
|
for _, ruleSet := range r.ruleSets {
|
|
monitor.Start("post start rule_set[", ruleSet.Name(), "]")
|
|
err := ruleSet.PostStart()
|
|
monitor.Finish()
|
|
if err != nil {
|
|
return E.Cause(err, "post start rule_set[", ruleSet.Name(), "]")
|
|
}
|
|
}
|
|
r.started = true
|
|
return nil
|
|
case adapter.StartStateStarted:
|
|
for _, ruleSet := range r.ruleSets {
|
|
ruleSet.Cleanup()
|
|
}
|
|
runtime.GC()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *Router) Close() error {
|
|
monitor := taskmonitor.New(r.logger, C.StopTimeout)
|
|
var err error
|
|
if r.neighborResolver != nil {
|
|
monitor.Start("close neighbor resolver")
|
|
err = E.Append(err, r.neighborResolver.Close(), func(closeErr error) error {
|
|
return E.Cause(closeErr, "close neighbor resolver")
|
|
})
|
|
monitor.Finish()
|
|
}
|
|
for i, rule := range r.rules {
|
|
monitor.Start("close rule[", i, "]")
|
|
err = E.Append(err, rule.Close(), func(err error) error {
|
|
return E.Cause(err, "close rule[", i, "]")
|
|
})
|
|
monitor.Finish()
|
|
}
|
|
for i, ruleSet := range r.ruleSets {
|
|
monitor.Start("close rule-set[", i, "]")
|
|
err = E.Append(err, ruleSet.Close(), func(err error) error {
|
|
return E.Cause(err, "close rule-set[", i, "]")
|
|
})
|
|
monitor.Finish()
|
|
}
|
|
if r.processSearcher != nil {
|
|
monitor.Start("close process searcher")
|
|
err = E.Append(err, r.processSearcher.Close(), func(err error) error {
|
|
return E.Cause(err, "close process searcher")
|
|
})
|
|
monitor.Finish()
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (r *Router) RuleSet(tag string) (adapter.RuleSet, bool) {
|
|
ruleSet, loaded := r.ruleSetMap[tag]
|
|
return ruleSet, loaded
|
|
}
|
|
|
|
func (r *Router) Rules() []adapter.Rule {
|
|
return r.rules
|
|
}
|
|
|
|
func (r *Router) AppendTracker(tracker adapter.ConnectionTracker) {
|
|
r.trackers = append(r.trackers, tracker)
|
|
}
|
|
|
|
func (r *Router) NeedFindProcess() bool {
|
|
return r.needFindProcess
|
|
}
|
|
|
|
func (r *Router) NeedFindNeighbor() bool {
|
|
return r.needFindNeighbor
|
|
}
|
|
|
|
func (r *Router) NeighborResolver() adapter.NeighborResolver {
|
|
return r.neighborResolver
|
|
}
|
|
|
|
func (r *Router) ResetNetwork() {
|
|
r.network.ResetNetwork()
|
|
r.dns.ResetNetwork()
|
|
}
|