This commit is contained in:
世界 2024-06-12 15:20:13 +08:00
parent c646fd6b2c
commit 307968bc2a
No known key found for this signature in database
GPG Key ID: CD109927C34A63C4
11 changed files with 302 additions and 82 deletions

View File

@ -10,15 +10,18 @@ import (
"github.com/sagernet/sing-tun"
"github.com/sagernet/sing/common/control"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/x/list"
"github.com/sagernet/sing/service"
mdns "github.com/miekg/dns"
"go4.org/netipx"
)
type Router interface {
Service
PreStarter
PostStarter
Cleanup() error
Outbounds() []Outbound
Outbound(tag string) (Outbound, bool)
@ -92,12 +95,21 @@ type DNSRule interface {
}
type RuleSet interface {
Name() string
StartContext(ctx context.Context, startContext RuleSetStartContext) error
Metadata() RuleSetMetadata
ExtractIPSet() []*netipx.IPSet
IncRef()
DecRef()
Cleanup()
RegisterCallback(callback RuleSetUpdateCallback) *list.Element[RuleSetUpdateCallback]
UnregisterCallback(element *list.Element[RuleSetUpdateCallback])
Close() error
HeadlessRule
}
type RuleSetUpdateCallback func(it RuleSet)
type RuleSetMetadata struct {
ContainsProcessRule bool
ContainsWIFIRule bool

28
box.go
View File

@ -303,7 +303,11 @@ func (s *Box) start() error {
return E.Cause(err, "initialize inbound/", in.Type(), "[", tag, "]")
}
}
return s.postStart()
err = s.postStart()
if err != nil {
return err
}
return s.router.Cleanup()
}
func (s *Box) postStart() error {
@ -313,16 +317,28 @@ func (s *Box) postStart() error {
return E.Cause(err, "start ", serviceName)
}
}
for _, outbound := range s.outbounds {
if lateOutbound, isLateOutbound := outbound.(adapter.PostStarter); isLateOutbound {
// TODO: reorganize ALL start order
for _, out := range s.outbounds {
if lateOutbound, isLateOutbound := out.(adapter.PostStarter); isLateOutbound {
err := lateOutbound.PostStart()
if err != nil {
return E.Cause(err, "post-start outbound/", outbound.Tag())
return E.Cause(err, "post-start outbound/", out.Tag())
}
}
}
return s.router.PostStart()
err := s.router.PostStart()
if err != nil {
return err
}
for _, in := range s.inbounds {
if lateInbound, isLateInbound := in.(adapter.PostStarter); isLateInbound {
err = lateInbound.PostStart()
if err != nil {
return E.Cause(err, "post-start inbound/", in.Tag())
}
}
}
return nil
}
func (s *Box) Close() error {

4
go.mod
View File

@ -33,7 +33,7 @@ require (
github.com/sagernet/sing-shadowsocks v0.2.6
github.com/sagernet/sing-shadowsocks2 v0.2.0
github.com/sagernet/sing-shadowtls v0.1.4
github.com/sagernet/sing-tun v0.4.0-beta.9
github.com/sagernet/sing-tun v0.4.0-beta.9.0.20240612071945-28df5b4b41f5
github.com/sagernet/sing-vmess v0.1.8
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6
@ -81,7 +81,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba // indirect
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
github.com/sagernet/nftables v0.3.0-beta.2 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect

8
go.sum
View File

@ -104,8 +104,8 @@ github.com/sagernet/gomobile v0.1.3 h1:ohjIb1Ou2+1558PnZour3od69suSuvkdSVOlO1tC4
github.com/sagernet/gomobile v0.1.3/go.mod h1:Pqq2+ZVvs10U7xK+UwJgwYWUykewi8H6vlslAO73n9E=
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f h1:NkhuupzH5ch7b/Y/6ZHJWrnNLoiNnSJaow6DPb8VW2I=
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f/go.mod h1:KXmw+ouSJNOsuRpg4wgwwCQuunrGz4yoAqQjsLjc6N0=
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba h1:EY5AS7CCtfmARNv2zXUOrsEMPFDGYxaw65JzA2p51Vk=
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/nftables v0.3.0-beta.2 h1:yKqMl4Dpb6nKxAmlE6fXjJRlLO2c1f2wyNFBg4hBr8w=
github.com/sagernet/nftables v0.3.0-beta.2/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8=
github.com/sagernet/quic-go v0.45.0-beta.2 h1:nWq9KJTR+cGU8UU4E20XNjdM6QgbLkBgpq+NCExg5RY=
@ -127,8 +127,8 @@ github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wK
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
github.com/sagernet/sing-tun v0.4.0-beta.9 h1:/5hXQ0u7tHtngfXozRc+o/gt6zfHBHMOwSIHXF0+S3I=
github.com/sagernet/sing-tun v0.4.0-beta.9/go.mod h1:uoRiCzWHzHLw/angVqXDzUNiQcMRl/ZrElJryQLJFhY=
github.com/sagernet/sing-tun v0.4.0-beta.9.0.20240612071945-28df5b4b41f5 h1:OpRvbxi+9zZ4LmWo1GqlWXGLOdCquFg5J3qh6uUwTBU=
github.com/sagernet/sing-tun v0.4.0-beta.9.0.20240612071945-28df5b4b41f5/go.mod h1:yaLwsfgGeRTsgupm3MOz/6r7EJ74qIgZCyKLNW/6aHE=
github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc=
github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA=
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=

View File

@ -20,25 +20,34 @@ import (
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/ranges"
"github.com/sagernet/sing/common/x/list"
"go4.org/netipx"
)
var _ adapter.Inbound = (*Tun)(nil)
type Tun struct {
tag string
ctx context.Context
router adapter.Router
logger log.ContextLogger
inboundOptions option.InboundOptions
tunOptions tun.Options
endpointIndependentNat bool
udpTimeout int64
stack string
tunIf tun.Tun
tunStack tun.Stack
platformInterface platform.Interface
platformOptions option.TunPlatformOptions
autoRedirect tun.AutoRedirect
tag string
ctx context.Context
router adapter.Router
logger log.ContextLogger
inboundOptions option.InboundOptions
tunOptions tun.Options
endpointIndependentNat bool
udpTimeout int64
stack string
tunIf tun.Tun
tunStack tun.Stack
platformInterface platform.Interface
platformOptions option.TunPlatformOptions
autoRedirect tun.AutoRedirect
routeRuleSet []adapter.RuleSet
routeRuleSetCallback []*list.Element[adapter.RuleSetUpdateCallback]
routeExcludeRuleSet []adapter.RuleSet
routeExcludeRuleSetCallback []*list.Element[adapter.RuleSetUpdateCallback]
routeAddressSet []*netipx.IPSet
routeExcludeAddressSet []*netipx.IPSet
}
func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TunInboundOptions, platformInterface platform.Interface) (*Tun, error) {
@ -81,6 +90,7 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
Inet4Address: options.Inet4Address,
Inet6Address: options.Inet6Address,
AutoRoute: options.AutoRoute,
AutoRedirect: options.AutoRedirect,
StrictRoute: options.StrictRoute,
IncludeInterface: options.IncludeInterface,
ExcludeInterface: options.ExcludeInterface,
@ -108,15 +118,33 @@ func NewTun(ctx context.Context, router adapter.Router, logger log.ContextLogger
}
disableNFTables, dErr := strconv.ParseBool(os.Getenv("DISABLE_NFTABLES"))
inbound.autoRedirect, err = tun.NewAutoRedirect(tun.AutoRedirectOptions{
TunOptions: &inbound.tunOptions,
Context: ctx,
Handler: inbound,
Logger: logger,
TableName: "sing-box",
DisableNFTables: dErr == nil && disableNFTables,
TunOptions: &inbound.tunOptions,
Context: ctx,
Handler: inbound,
Logger: logger,
TableName: "sing-box",
DisableNFTables: dErr == nil && disableNFTables,
RouteAddressSet: &inbound.routeAddressSet,
RouteExcludeAddressSet: &inbound.routeExcludeAddressSet,
})
if err != nil {
return nil, E.Cause(err, "initialize auto redirect")
return nil, E.Cause(err, "initialize auto-redirect")
}
for _, routeAddressSet := range options.RouteAddressSet {
ruleSet, loaded := router.RuleSet(routeAddressSet)
if !loaded {
return nil, E.New("parse route_address_set: rule-set not found: ", routeAddressSet)
}
ruleSet.IncRef()
inbound.routeRuleSet = append(inbound.routeRuleSet, ruleSet)
}
for _, routeExcludeAddressSet := range options.RouteExcludeAddressSet {
ruleSet, loaded := router.RuleSet(routeExcludeAddressSet)
if !loaded {
return nil, E.New("parse route_exclude_address_set: rule-set not found: ", routeExcludeAddressSet)
}
ruleSet.IncRef()
inbound.routeExcludeRuleSet = append(inbound.routeExcludeRuleSet, ruleSet)
}
}
return inbound, nil
@ -215,18 +243,45 @@ func (t *Tun) Start() error {
if err != nil {
return err
}
if t.autoRedirect != nil {
monitor.Start("initiating auto redirect")
err = t.autoRedirect.Start()
monitor.Finish()
if err != nil {
return E.Cause(err, "auto redirect")
}
}
t.logger.Info("started at ", t.tunOptions.Name)
return nil
}
func (t *Tun) PostStart() error {
monitor := taskmonitor.New(t.logger, C.StartTimeout)
// TODO: add ref counter to rule-set and release them if not needed
for _, ruleSet := range t.routeRuleSet {
t.routeAddressSet = append(t.routeAddressSet, ruleSet.ExtractIPSet()...)
}
for _, ruleSet := range t.routeExcludeRuleSet {
t.routeExcludeAddressSet = append(t.routeExcludeAddressSet, ruleSet.ExtractIPSet()...)
}
if t.autoRedirect != nil {
monitor.Start("initiating auto-redirect")
err := t.autoRedirect.Start()
monitor.Finish()
if err != nil {
return E.Cause(err, "auto-redirect")
}
}
for _, routeRuleSet := range t.routeRuleSet {
t.routeRuleSetCallback = append(t.routeRuleSetCallback, routeRuleSet.RegisterCallback(t.updateRouteAddressSet))
routeRuleSet.DecRef()
}
for _, routeExcludeRuleSet := range t.routeExcludeRuleSet {
t.routeExcludeRuleSetCallback = append(t.routeExcludeRuleSetCallback, routeExcludeRuleSet.RegisterCallback(t.updateRouteAddressSet))
routeExcludeRuleSet.DecRef()
}
return nil
}
func (t *Tun) updateRouteAddressSet(it adapter.RuleSet) {
err := t.autoRedirect.UpdateRouteAddressSet()
if err != nil {
t.logger.Error("update route address set ", it.Name(), ": ", err)
}
}
func (t *Tun) Close() error {
return common.Close(
t.tunStack,

View File

@ -3,30 +3,42 @@ package option
import "net/netip"
type TunInboundOptions struct {
InterfaceName string `json:"interface_name,omitempty"`
MTU uint32 `json:"mtu,omitempty"`
GSO bool `json:"gso,omitempty"`
Inet4Address Listable[netip.Prefix] `json:"inet4_address,omitempty"`
Inet6Address Listable[netip.Prefix] `json:"inet6_address,omitempty"`
AutoRoute bool `json:"auto_route,omitempty"`
AutoRedirect bool `json:"auto_redirect,omitempty"`
StrictRoute bool `json:"strict_route,omitempty"`
Inet4RouteAddress Listable[netip.Prefix] `json:"inet4_route_address,omitempty"`
Inet6RouteAddress Listable[netip.Prefix] `json:"inet6_route_address,omitempty"`
Inet4RouteExcludeAddress Listable[netip.Prefix] `json:"inet4_route_exclude_address,omitempty"`
Inet6RouteExcludeAddress Listable[netip.Prefix] `json:"inet6_route_exclude_address,omitempty"`
IncludeInterface Listable[string] `json:"include_interface,omitempty"`
ExcludeInterface Listable[string] `json:"exclude_interface,omitempty"`
IncludeUID Listable[uint32] `json:"include_uid,omitempty"`
IncludeUIDRange Listable[string] `json:"include_uid_range,omitempty"`
ExcludeUID Listable[uint32] `json:"exclude_uid,omitempty"`
ExcludeUIDRange Listable[string] `json:"exclude_uid_range,omitempty"`
IncludeAndroidUser Listable[int] `json:"include_android_user,omitempty"`
IncludePackage Listable[string] `json:"include_package,omitempty"`
ExcludePackage Listable[string] `json:"exclude_package,omitempty"`
EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"`
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
Stack string `json:"stack,omitempty"`
Platform *TunPlatformOptions `json:"platform,omitempty"`
InterfaceName string `json:"interface_name,omitempty"`
MTU uint32 `json:"mtu,omitempty"`
GSO bool `json:"gso,omitempty"`
Address Listable[netip.Prefix] `json:"address,omitempty"`
AutoRoute bool `json:"auto_route,omitempty"`
AutoRedirect bool `json:"auto_redirect,omitempty"`
StrictRoute bool `json:"strict_route,omitempty"`
RouteAddress Listable[netip.Prefix] `json:"route_address,omitempty"`
RouteAddressSet Listable[string] `json:"route_address_set,omitempty"`
RouteExcludeAddress Listable[netip.Prefix] `json:"route_exclude_address,omitempty"`
RouteExcludeAddressSet Listable[string] `json:"route_exclude_address_set,omitempty"`
IncludeInterface Listable[string] `json:"include_interface,omitempty"`
ExcludeInterface Listable[string] `json:"exclude_interface,omitempty"`
IncludeUID Listable[uint32] `json:"include_uid,omitempty"`
IncludeUIDRange Listable[string] `json:"include_uid_range,omitempty"`
ExcludeUID Listable[uint32] `json:"exclude_uid,omitempty"`
ExcludeUIDRange Listable[string] `json:"exclude_uid_range,omitempty"`
IncludeAndroidUser Listable[int] `json:"include_android_user,omitempty"`
IncludePackage Listable[string] `json:"include_package,omitempty"`
ExcludePackage Listable[string] `json:"exclude_package,omitempty"`
EndpointIndependentNat bool `json:"endpoint_independent_nat,omitempty"`
UDPTimeout UDPTimeoutCompat `json:"udp_timeout,omitempty"`
Stack string `json:"stack,omitempty"`
Platform *TunPlatformOptions `json:"platform,omitempty"`
InboundOptions
// Deprecated: merged to `address`
Inet4Address Listable[netip.Prefix] `json:"inet4_address,omitempty"`
// Deprecated: merged to `address`
Inet6Address Listable[netip.Prefix] `json:"inet6_address,omitempty"`
// Deprecated: merged to `route_address`
Inet4RouteAddress Listable[netip.Prefix] `json:"inet4_route_address,omitempty"`
// Deprecated: merged to `route_address`
Inet6RouteAddress Listable[netip.Prefix] `json:"inet6_route_address,omitempty"`
// Deprecated: merged to `route_exclude_address`
Inet4RouteExcludeAddress Listable[netip.Prefix] `json:"inet4_route_exclude_address,omitempty"`
// Deprecated: merged to `route_exclude_address`
Inet6RouteExcludeAddress Listable[netip.Prefix] `json:"inet6_route_exclude_address,omitempty"`
}

View File

@ -728,6 +728,14 @@ func (r *Router) PostStart() error {
return nil
}
func (r *Router) Cleanup() error {
for _, ruleSet := range r.ruleSetMap {
ruleSet.Cleanup()
}
runtime.GC()
return nil
}
func (r *Router) Outbound(tag string) (adapter.Outbound, bool) {
outbound, loaded := r.outboundByTag[tag]
return outbound, loaded

View File

@ -32,6 +32,7 @@ func (r *RuleSetItem) Start() error {
if !loaded {
return E.New("rule-set not found: ", tag)
}
ruleSet.IncRef()
r.setList = append(r.setList, ruleSet)
}
return nil

View File

@ -9,10 +9,13 @@ import (
"github.com/sagernet/sing-box/adapter"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"go4.org/netipx"
)
func NewRuleSet(ctx context.Context, router adapter.Router, logger logger.ContextLogger, options option.RuleSet) (adapter.RuleSet, error) {
@ -26,6 +29,24 @@ func NewRuleSet(ctx context.Context, router adapter.Router, logger logger.Contex
}
}
func extractIPSetFromRule(rawRule adapter.HeadlessRule) []*netipx.IPSet {
switch rule := rawRule.(type) {
case *DefaultHeadlessRule:
return common.FlatMap(rule.destinationIPCIDRItems, func(rawItem RuleItem) []*netipx.IPSet {
switch item := rawItem.(type) {
case *IPCIDRItem:
return []*netipx.IPSet{item.ipSet}
default:
return nil
}
})
case *LogicalHeadlessRule:
return common.FlatMap(rule.rules, extractIPSetFromRule)
default:
panic("unexpected rule type")
}
}
var _ adapter.RuleSetStartContext = (*RuleSetStartContext)(nil)
type RuleSetStartContext struct {

View File

@ -9,16 +9,23 @@ import (
"github.com/sagernet/sing-box/common/srs"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/atomic"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
"github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/x/list"
"go4.org/netipx"
)
var _ adapter.RuleSet = (*LocalRuleSet)(nil)
type LocalRuleSet struct {
tag string
rules []adapter.HeadlessRule
metadata adapter.RuleSetMetadata
refs atomic.Int32
}
func NewLocalRuleSet(router adapter.Router, options option.RuleSet) (*LocalRuleSet, error) {
@ -58,16 +65,11 @@ func NewLocalRuleSet(router adapter.Router, options option.RuleSet) (*LocalRuleS
metadata.ContainsProcessRule = hasHeadlessRule(plainRuleSet.Rules, isProcessHeadlessRule)
metadata.ContainsWIFIRule = hasHeadlessRule(plainRuleSet.Rules, isWIFIHeadlessRule)
metadata.ContainsIPCIDRRule = hasHeadlessRule(plainRuleSet.Rules, isIPCIDRHeadlessRule)
return &LocalRuleSet{rules, metadata}, nil
return &LocalRuleSet{tag: options.Tag, rules: rules, metadata: metadata}, nil
}
func (s *LocalRuleSet) Match(metadata *adapter.InboundContext) bool {
for _, rule := range s.rules {
if rule.Match(metadata) {
return true
}
}
return false
func (s *LocalRuleSet) Name() string {
return s.tag
}
func (s *LocalRuleSet) String() string {
@ -82,6 +84,43 @@ func (s *LocalRuleSet) Metadata() adapter.RuleSetMetadata {
return s.metadata
}
func (s *LocalRuleSet) Close() error {
func (s *LocalRuleSet) ExtractIPSet() []*netipx.IPSet {
return common.FlatMap(s.rules, extractIPSetFromRule)
}
func (s *LocalRuleSet) IncRef() {
s.refs.Add(1)
}
func (s *LocalRuleSet) DecRef() {
if s.refs.Add(-1) < 0 {
panic("rule-set: negative refs")
}
}
func (s *LocalRuleSet) Cleanup() {
if s.refs.Load() == 0 {
s.rules = nil
}
}
func (s *LocalRuleSet) RegisterCallback(callback adapter.RuleSetUpdateCallback) *list.Element[adapter.RuleSetUpdateCallback] {
return nil
}
func (s *LocalRuleSet) UnregisterCallback(element *list.Element[adapter.RuleSetUpdateCallback]) {
}
func (s *LocalRuleSet) Close() error {
s.rules = nil
return nil
}
func (s *LocalRuleSet) Match(metadata *adapter.InboundContext) bool {
for _, rule := range s.rules {
if rule.Match(metadata) {
return true
}
}
return false
}

View File

@ -8,20 +8,26 @@ import (
"net/http"
"runtime"
"strings"
"sync"
"time"
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing-box/common/srs"
C "github.com/sagernet/sing-box/constant"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/atomic"
E "github.com/sagernet/sing/common/exceptions"
F "github.com/sagernet/sing/common/format"
"github.com/sagernet/sing/common/json"
"github.com/sagernet/sing/common/logger"
M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"github.com/sagernet/sing/common/x/list"
"github.com/sagernet/sing/service"
"github.com/sagernet/sing/service/pause"
"go4.org/netipx"
)
var _ adapter.RuleSet = (*RemoteRuleSet)(nil)
@ -40,6 +46,9 @@ type RemoteRuleSet struct {
lastEtag string
updateTicker *time.Ticker
pauseManager pause.Manager
callbackAccess sync.Mutex
callbacks list.List[adapter.RuleSetUpdateCallback]
refs atomic.Int32
}
func NewRemoteRuleSet(ctx context.Context, router adapter.Router, logger logger.ContextLogger, options option.RuleSet) *RemoteRuleSet {
@ -61,13 +70,8 @@ func NewRemoteRuleSet(ctx context.Context, router adapter.Router, logger logger.
}
}
func (s *RemoteRuleSet) Match(metadata *adapter.InboundContext) bool {
for _, rule := range s.rules {
if rule.Match(metadata) {
return true
}
}
return false
func (s *RemoteRuleSet) Name() string {
return s.options.Tag
}
func (s *RemoteRuleSet) String() string {
@ -116,6 +120,38 @@ func (s *RemoteRuleSet) Metadata() adapter.RuleSetMetadata {
return s.metadata
}
func (s *RemoteRuleSet) ExtractIPSet() []*netipx.IPSet {
return common.FlatMap(s.rules, extractIPSetFromRule)
}
func (s *RemoteRuleSet) IncRef() {
s.refs.Add(1)
}
func (s *RemoteRuleSet) DecRef() {
if s.refs.Add(-1) < 0 {
panic("rule-set: negative refs")
}
}
func (s *RemoteRuleSet) Cleanup() {
if s.refs.Load() == 0 {
s.rules = nil
}
}
func (s *RemoteRuleSet) RegisterCallback(callback adapter.RuleSetUpdateCallback) *list.Element[adapter.RuleSetUpdateCallback] {
s.callbackAccess.Lock()
defer s.callbackAccess.Unlock()
return s.callbacks.PushBack(callback)
}
func (s *RemoteRuleSet) UnregisterCallback(element *list.Element[adapter.RuleSetUpdateCallback]) {
s.callbackAccess.Lock()
defer s.callbackAccess.Unlock()
s.callbacks.Remove(element)
}
func (s *RemoteRuleSet) loadBytes(content []byte) error {
var (
plainRuleSet option.PlainRuleSet
@ -148,6 +184,16 @@ func (s *RemoteRuleSet) loadBytes(content []byte) error {
s.metadata.ContainsWIFIRule = hasHeadlessRule(plainRuleSet.Rules, isWIFIHeadlessRule)
s.metadata.ContainsIPCIDRRule = hasHeadlessRule(plainRuleSet.Rules, isIPCIDRHeadlessRule)
s.rules = rules
s.callbackAccess.Lock()
callbacks := s.callbacks.Array()
s.callbackAccess.Unlock()
for _, callback := range callbacks {
callback(s)
}
if s.refs.Load() == 0 {
s.rules = nil
runtime.GC()
}
return nil
}
@ -253,7 +299,17 @@ func (s *RemoteRuleSet) fetchOnce(ctx context.Context, startContext adapter.Rule
}
func (s *RemoteRuleSet) Close() error {
s.rules = nil
s.updateTicker.Stop()
s.cancel()
return nil
}
func (s *RemoteRuleSet) Match(metadata *adapter.InboundContext) bool {
for _, rule := range s.rules {
if rule.Match(metadata) {
return true
}
}
return false
}