mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-01-30 04:36:54 +00:00
207 lines
6 KiB
Go
207 lines
6 KiB
Go
package route
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"runtime"
|
|
|
|
"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/experimental/libbox/platform"
|
|
"github.com/sagernet/sing-box/log"
|
|
"github.com/sagernet/sing-box/option"
|
|
R "github.com/sagernet/sing-box/route/rule"
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
"github.com/sagernet/sing/common/task"
|
|
"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
|
|
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 {
|
|
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),
|
|
}
|
|
}
|
|
|
|
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, "]")
|
|
}
|
|
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()
|
|
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()
|
|
}
|
|
needFindProcess := r.needFindProcess
|
|
for _, ruleSet := range r.ruleSets {
|
|
metadata := ruleSet.Metadata()
|
|
if metadata.ContainsProcessRule {
|
|
needFindProcess = true
|
|
}
|
|
if metadata.ContainsWIFIRule {
|
|
r.needWIFIState = true
|
|
}
|
|
}
|
|
if needFindProcess {
|
|
if r.platformInterface != nil {
|
|
r.processSearcher = 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
|
|
}
|
|
}
|
|
}
|
|
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.ruleSetMap {
|
|
ruleSet.Cleanup()
|
|
}
|
|
runtime.GC()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *Router) Close() error {
|
|
monitor := taskmonitor.New(r.logger, C.StopTimeout)
|
|
var err error
|
|
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()
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (r *Router) RuleSet(tag string) (adapter.RuleSet, bool) {
|
|
ruleSet, loaded := r.ruleSetMap[tag]
|
|
return ruleSet, loaded
|
|
}
|
|
|
|
func (r *Router) NeedWIFIState() bool {
|
|
return r.needWIFIState
|
|
}
|
|
|
|
func (r *Router) Rules() []adapter.Rule {
|
|
return r.rules
|
|
}
|
|
|
|
func (r *Router) SetTracker(tracker adapter.ConnectionTracker) {
|
|
r.tracker = tracker
|
|
}
|
|
|
|
func (r *Router) ResetNetwork() {
|
|
r.network.ResetNetwork()
|
|
r.dns.ResetNetwork()
|
|
}
|