From 6b5ee55ba204fdff11e4c38b5e73853f08914483 Mon Sep 17 00:00:00 2001 From: Shtorm <108103062+shtorm-7@users.noreply.github.com> Date: Mon, 19 Oct 2026 14:37:32 +0300 Subject: [PATCH] fix: Report fork component references to reference manager --- adapter/provider/adapter.go | 4 ++++ protocol/bond/inbound.go | 11 +++++++++ protocol/bond/outbound.go | 31 +++++++++++++++++++++++++ protocol/failover/inbound.go | 11 +++++++++ protocol/failover/outbound.go | 31 +++++++++++++++++++++++++ protocol/limiter/bandwidth/outbound.go | 4 ++++ protocol/limiter/connection/outbound.go | 4 ++++ protocol/limiter/rate/outbound.go | 4 ++++ protocol/limiter/traffic/outbound.go | 4 ++++ protocol/vpn/client.go | 8 +++++++ protocol/vpn/server.go | 11 +++++++++ provider/remote/provider.go | 11 +++++++++ route/reference.go | 7 ++++++ route/router.go | 16 +++++++++++++ 14 files changed, 157 insertions(+) diff --git a/adapter/provider/adapter.go b/adapter/provider/adapter.go index 1a3a93ed..c834223f 100644 --- a/adapter/provider/adapter.go +++ b/adapter/provider/adapter.go @@ -186,6 +186,10 @@ func (a *Adapter) UpdateGroups() { for element := a.callbacks.Front(); element != nil; element = element.Next() { element.Value(a.providerTag) } + history := service.PtrFromContext[urltest.HistoryStorage](a.ctx) + if history != nil { + history.NotifyUpdated() + } } func (a *Adapter) Close() error { diff --git a/protocol/bond/inbound.go b/protocol/bond/inbound.go index 60359ac4..ec7496d9 100644 --- a/protocol/bond/inbound.go +++ b/protocol/bond/inbound.go @@ -84,6 +84,17 @@ func (h *Inbound) Start(stage adapter.StartStage, scope *adapter.Scope) error { return nil } +func (h *Inbound) References() []string { + var references []string + for _, inbound := range h.inbounds { + referrer, isReferrer := inbound.(adapter.Referrer) + if isReferrer { + references = append(references, referrer.References()...) + } + } + return references +} + func (h *Inbound) connHandler(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) error { request, err := ReadRequest(conn) if err != nil { diff --git a/protocol/bond/outbound.go b/protocol/bond/outbound.go index 604b9c41..a8da970e 100644 --- a/protocol/bond/outbound.go +++ b/protocol/bond/outbound.go @@ -89,6 +89,37 @@ func (h *Outbound) Start(stage adapter.StartStage, scope *adapter.Scope) error { return nil } +func (h *Outbound) References() []string { + var references []string + for _, outbound := range h.outbounds { + referrer, isReferrer := outbound.(adapter.Referrer) + if isReferrer { + references = append(references, referrer.References()...) + } else { + references = append(references, outbound.Dependencies()...) + } + } + return references +} + +func (h *Outbound) SetKeepIdleConnections(keep bool) { + for _, outbound := range h.outbounds { + keeper, isKeeper := outbound.(adapter.IdleConnectionKeeper) + if isKeeper { + keeper.SetKeepIdleConnections(keep) + } + } +} + +func (h *Outbound) CloseIdleConnections() { + for _, outbound := range h.outbounds { + keeper, isKeeper := outbound.(adapter.IdleConnectionKeeper) + if isKeeper { + keeper.CloseIdleConnections() + } + } +} + func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { if N.NetworkName(network) == N.NetworkUDP { return h.uotClient.DialContext(ctx, network, destination) diff --git a/protocol/failover/inbound.go b/protocol/failover/inbound.go index 2b14bf89..36069a6e 100644 --- a/protocol/failover/inbound.go +++ b/protocol/failover/inbound.go @@ -69,6 +69,17 @@ func (h *Inbound) Start(stage adapter.StartStage, scope *adapter.Scope) error { return nil } +func (h *Inbound) References() []string { + var references []string + for _, inbound := range h.inbounds { + referrer, isReferrer := inbound.(adapter.Referrer) + if isReferrer { + references = append(references, referrer.References()...) + } + } + return references +} + func (h *Inbound) connHandler(ctx context.Context, conn net.Conn, metadata adapter.InboundContext, onClose N.CloseHandlerFunc) error { if metadata.Destination != Destination { h.router.RouteConnectionEx(ctx, conn, metadata, onClose) diff --git a/protocol/failover/outbound.go b/protocol/failover/outbound.go index 81075298..2d4064f7 100644 --- a/protocol/failover/outbound.go +++ b/protocol/failover/outbound.go @@ -79,6 +79,37 @@ func (f *Failover) Start(stage adapter.StartStage, scope *adapter.Scope) error { return nil } +func (f *Failover) References() []string { + var references []string + for _, outbound := range f.outbounds { + referrer, isReferrer := outbound.(adapter.Referrer) + if isReferrer { + references = append(references, referrer.References()...) + } else { + references = append(references, outbound.Dependencies()...) + } + } + return references +} + +func (f *Failover) SetKeepIdleConnections(keep bool) { + for _, outbound := range f.outbounds { + keeper, isKeeper := outbound.(adapter.IdleConnectionKeeper) + if isKeeper { + keeper.SetKeepIdleConnections(keep) + } + } +} + +func (f *Failover) CloseIdleConnections() { + for _, outbound := range f.outbounds { + keeper, isKeeper := outbound.(adapter.IdleConnectionKeeper) + if isKeeper { + keeper.CloseIdleConnections() + } + } +} + func (f *Failover) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { if N.NetworkName(network) == N.NetworkUDP { return f.uotClient.DialContext(ctx, network, destination) diff --git a/protocol/limiter/bandwidth/outbound.go b/protocol/limiter/bandwidth/outbound.go index f86cc793..fdd83fd5 100644 --- a/protocol/limiter/bandwidth/outbound.go +++ b/protocol/limiter/bandwidth/outbound.go @@ -100,6 +100,10 @@ func (h *Outbound) Start(stage adapter.StartStage, scope *adapter.Scope) error { return h.router.Start(stage, scope) } +func (h *Outbound) References() []string { + return h.router.References() +} + func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { conn, err := h.detour.DialContext(ctx, network, destination) if err != nil { diff --git a/protocol/limiter/connection/outbound.go b/protocol/limiter/connection/outbound.go index 7eea951d..bb01bd47 100644 --- a/protocol/limiter/connection/outbound.go +++ b/protocol/limiter/connection/outbound.go @@ -100,6 +100,10 @@ func (h *Outbound) Start(stage adapter.StartStage, scope *adapter.Scope) error { return h.router.Start(stage, scope) } +func (h *Outbound) References() []string { + return h.router.References() +} + func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { onClose, lockCtx, err := h.strategy.request(ctx, adapter.ContextFrom(ctx)) if err != nil { diff --git a/protocol/limiter/rate/outbound.go b/protocol/limiter/rate/outbound.go index 85dd7f74..76ddd511 100644 --- a/protocol/limiter/rate/outbound.go +++ b/protocol/limiter/rate/outbound.go @@ -96,6 +96,10 @@ func (h *Outbound) Start(stage adapter.StartStage, scope *adapter.Scope) error { return h.router.Start(stage, scope) } +func (h *Outbound) References() []string { + return h.router.References() +} + func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { if err := h.strategy.request(ctx, adapter.ContextFrom(ctx)); err != nil { return nil, err diff --git a/protocol/limiter/traffic/outbound.go b/protocol/limiter/traffic/outbound.go index de26529e..5f3d4654 100644 --- a/protocol/limiter/traffic/outbound.go +++ b/protocol/limiter/traffic/outbound.go @@ -76,6 +76,10 @@ func (h *Outbound) Start(stage adapter.StartStage, scope *adapter.Scope) error { return h.router.Start(stage, scope) } +func (h *Outbound) References() []string { + return h.router.References() +} + func (h *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { conn, err := h.detour.DialContext(ctx, network, destination) if err != nil { diff --git a/protocol/vpn/client.go b/protocol/vpn/client.go index fc88d8f3..90d2c614 100644 --- a/protocol/vpn/client.go +++ b/protocol/vpn/client.go @@ -106,6 +106,14 @@ func (c *ClientEndpoint) Start(stage adapter.StartStage, scope *adapter.Scope) e return nil } +func (c *ClientEndpoint) References() []string { + referrer, isReferrer := c.outbound.(adapter.Referrer) + if isReferrer { + return referrer.References() + } + return c.outbound.Dependencies() +} + func (c *ClientEndpoint) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { if N.NetworkName(network) == N.NetworkUDP { return c.uotClient.DialContext(ctx, network, destination) diff --git a/protocol/vpn/server.go b/protocol/vpn/server.go index b7dec47c..aaf143ad 100644 --- a/protocol/vpn/server.go +++ b/protocol/vpn/server.go @@ -107,6 +107,17 @@ func (s *ServerEndpoint) Start(stage adapter.StartStage, scope *adapter.Scope) e return nil } +func (s *ServerEndpoint) References() []string { + var references []string + for _, inbound := range s.inbounds { + referrer, isReferrer := inbound.(adapter.Referrer) + if isReferrer { + references = append(references, referrer.References()...) + } + } + return references +} + func (s *ServerEndpoint) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { if N.NetworkName(network) == N.NetworkUDP { return s.uotClient.DialContext(ctx, network, destination) diff --git a/provider/remote/provider.go b/provider/remote/provider.go index 4910e906..3b9a3e07 100644 --- a/provider/remote/provider.go +++ b/provider/remote/provider.go @@ -140,6 +140,17 @@ func (s *ProviderRemote) Start(stage adapter.StartStage, scope *adapter.Scope) e return s.Adapter.Start() } +func (s *ProviderRemote) References() []string { + if s.downloadDetour != "" { + return []string{s.downloadDetour} + } + defaultOutbound := s.outbound.Default() + if defaultOutbound == nil { + return nil + } + return []string{defaultOutbound.Tag()} +} + func (s *ProviderRemote) Update() error { if s.ticker != nil { s.ticker.Reset(s.updateInterval) diff --git a/route/reference.go b/route/reference.go index ac4511f9..68df06bb 100644 --- a/route/reference.go +++ b/route/reference.go @@ -121,6 +121,7 @@ func (m *ReferenceManager) update() { transportManager := service.FromContext[adapter.DNSTransportManager](m.ctx) networkManager := service.FromContext[adapter.NetworkManager](m.ctx) httpClientManager := service.FromContext[adapter.HTTPClientManager](m.ctx) + providerManager := service.FromContext[adapter.ProviderManager](m.ctx) transportQueue := slices.Clone(m.staticTransports) outboundQueue := slices.Clone(m.staticOutbounds) @@ -156,6 +157,12 @@ func (m *ReferenceManager) update() { if isReferrer { outboundQueue = append(outboundQueue, referrer.References()...) } + for _, provider := range providerManager.Providers() { + referrer, isReferrer := provider.(adapter.Referrer) + if isReferrer { + outboundQueue = append(outboundQueue, referrer.References()...) + } + } referencedTransports := make(map[string]bool) for len(transportQueue) > 0 { diff --git a/route/router.go b/route/router.go index 0ee54df0..1e2f0ef6 100644 --- a/route/router.go +++ b/route/router.go @@ -10,6 +10,7 @@ import ( "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/clashmode" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" R "github.com/sagernet/sing-box/route/rule" @@ -36,6 +37,7 @@ type Router struct { defaultOutbound adapter.Outbound httpClientManager adapter.HTTPClientManager rules []adapter.Rule + ruleOptions []option.Rule final string needFindProcess bool needFindNeighbor bool @@ -64,6 +66,7 @@ func NewRouter(ctx context.Context, logFactory log.Factory, name string, options network: service.FromContext[adapter.NetworkManager](ctx), httpClientManager: service.FromContext[adapter.HTTPClientManager](ctx), rules: make([]adapter.Rule, 0, len(options.Rules)), + ruleOptions: options.Rules, final: options.Final, ruleSetMap: make(map[string]adapter.RuleSet), needFindProcess: hasRule(options.Rules, isProcessRule) || hasDNSRule(dnsOptions.Rules, isProcessDNSRule) || options.FindProcess, @@ -242,6 +245,19 @@ func (r *Router) Start(stage adapter.StartStage, scope *adapter.Scope) error { return nil } +func (r *Router) References() []string { + var mode string + clashMode := service.PtrFromContext[clashmode.Manager](r.ctx) + if clashMode != nil { + mode = clashMode.Mode() + } + var references, transports []string + if !collectRuleReferences(r.ruleOptions, mode, &references, &transports) { + references = append(references, r.final) + } + return references +} + func (r *Router) RuleSet(tag string) (adapter.RuleSet, bool) { ruleSet, loaded := r.ruleSetMap[tag] return ruleSet, loaded