diff --git a/docs/changelog.md b/docs/changelog.md index dca53ad4..aab409a7 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -8,6 +8,12 @@ icon: material/alert-decagram * Fixes and improvements +#### 1.6.7 + +* macOS: Add button for uninstall SystemExtension in the standalone graphical client +* Fix missing UDP user context on TUIC/Hysteria2 inbounds +* Fixes and improvements + #### 1.7.0-rc.2 * Fix missing UDP user context on TUIC/Hysteria2 inbounds diff --git a/experimental/clashapi.go b/experimental/clashapi.go index 894d40a7..8f2e18fe 100644 --- a/experimental/clashapi.go +++ b/experimental/clashapi.go @@ -5,6 +5,7 @@ import ( "os" "github.com/sagernet/sing-box/adapter" + C "github.com/sagernet/sing-box/constant" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing/common" @@ -27,24 +28,35 @@ func NewClashServer(ctx context.Context, router adapter.Router, logFactory log.O func CalculateClashModeList(options option.Options) []string { var clashMode []string - for _, dnsRule := range common.PtrValueOrDefault(options.DNS).Rules { - if dnsRule.DefaultOptions.ClashMode != "" && !common.Contains(clashMode, dnsRule.DefaultOptions.ClashMode) { - clashMode = append(clashMode, dnsRule.DefaultOptions.ClashMode) - } - for _, defaultRule := range dnsRule.LogicalOptions.Rules { - if defaultRule.ClashMode != "" && !common.Contains(clashMode, defaultRule.ClashMode) { - clashMode = append(clashMode, defaultRule.ClashMode) - } - } - } - for _, rule := range common.PtrValueOrDefault(options.Route).Rules { - if rule.DefaultOptions.ClashMode != "" && !common.Contains(clashMode, rule.DefaultOptions.ClashMode) { + clashMode = append(clashMode, extraClashModeFromRule(common.PtrValueOrDefault(options.Route).Rules)...) + clashMode = append(clashMode, extraClashModeFromDNSRule(common.PtrValueOrDefault(options.DNS).Rules)...) + clashMode = common.Uniq(clashMode) + return clashMode +} + +func extraClashModeFromRule(rules []option.Rule) []string { + var clashMode []string + for _, rule := range rules { + switch rule.Type { + case C.RuleTypeDefault: clashMode = append(clashMode, rule.DefaultOptions.ClashMode) - } - for _, defaultRule := range rule.LogicalOptions.Rules { - if defaultRule.ClashMode != "" && !common.Contains(clashMode, defaultRule.ClashMode) { - clashMode = append(clashMode, defaultRule.ClashMode) - } + case C.RuleTypeLogical: + clashMode = append(clashMode, extraClashModeFromRule(rule.LogicalOptions.Rules)...) + } + } + return clashMode +} + +func extraClashModeFromDNSRule(rules []option.DNSRule) []string { + var clashMode []string + for _, rule := range rules { + switch rule.Type { + case C.RuleTypeDefault: + if rule.DefaultOptions.ClashMode != "" { + clashMode = append(clashMode, rule.DefaultOptions.ClashMode) + } + case C.RuleTypeLogical: + clashMode = append(clashMode, extraClashModeFromDNSRule(rule.LogicalOptions.Rules)...) } } return clashMode diff --git a/experimental/clashapi/server.go b/experimental/clashapi/server.go index 135ff3ef..c40ff938 100644 --- a/experimental/clashapi/server.go +++ b/experimental/clashapi/server.go @@ -85,6 +85,7 @@ func NewServer(ctx context.Context, router adapter.Router, logFactory log.Observ } server.mode = defaultMode //goland:noinspection GoDeprecation + //nolint:staticcheck if options.StoreMode || options.StoreSelected || options.StoreFakeIP || options.CacheFile != "" || options.CacheID != "" { return nil, E.New("cache_file and related fields in Clash API is deprecated in sing-box 1.8.0, use experimental.cache_file instead.") } diff --git a/option/rule.go b/option/rule.go index 8caba96e..4f404202 100644 --- a/option/rule.go +++ b/option/rule.go @@ -53,6 +53,17 @@ func (r *Rule) UnmarshalJSON(bytes []byte) error { return nil } +func (r Rule) IsValid() bool { + switch r.Type { + case C.RuleTypeDefault: + return r.DefaultOptions.IsValid() + case C.RuleTypeLogical: + return r.LogicalOptions.IsValid() + default: + panic("unknown rule type: " + r.Type) + } +} + type DefaultRule struct { Inbound Listable[string] `json:"inbound,omitempty"` IPVersion int `json:"ip_version,omitempty"` @@ -92,12 +103,12 @@ func (r DefaultRule) IsValid() bool { } type LogicalRule struct { - Mode string `json:"mode"` - Rules []DefaultRule `json:"rules,omitempty"` - Invert bool `json:"invert,omitempty"` - Outbound string `json:"outbound,omitempty"` + Mode string `json:"mode"` + Rules []Rule `json:"rules,omitempty"` + Invert bool `json:"invert,omitempty"` + Outbound string `json:"outbound,omitempty"` } func (r LogicalRule) IsValid() bool { - return len(r.Rules) > 0 && common.All(r.Rules, DefaultRule.IsValid) + return len(r.Rules) > 0 && common.All(r.Rules, Rule.IsValid) } diff --git a/option/rule_dns.go b/option/rule_dns.go index ba572b9a..fca34322 100644 --- a/option/rule_dns.go +++ b/option/rule_dns.go @@ -53,6 +53,17 @@ func (r *DNSRule) UnmarshalJSON(bytes []byte) error { return nil } +func (r DNSRule) IsValid() bool { + switch r.Type { + case C.RuleTypeDefault: + return r.DefaultOptions.IsValid() + case C.RuleTypeLogical: + return r.LogicalOptions.IsValid() + default: + panic("unknown DNS rule type: " + r.Type) + } +} + type DefaultDNSRule struct { Inbound Listable[string] `json:"inbound,omitempty"` IPVersion int `json:"ip_version,omitempty"` @@ -96,14 +107,14 @@ func (r DefaultDNSRule) IsValid() bool { } type LogicalDNSRule struct { - Mode string `json:"mode"` - Rules []DefaultDNSRule `json:"rules,omitempty"` - Invert bool `json:"invert,omitempty"` - Server string `json:"server,omitempty"` - DisableCache bool `json:"disable_cache,omitempty"` - RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"` + Mode string `json:"mode"` + Rules []DNSRule `json:"rules,omitempty"` + Invert bool `json:"invert,omitempty"` + Server string `json:"server,omitempty"` + DisableCache bool `json:"disable_cache,omitempty"` + RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"` } func (r LogicalDNSRule) IsValid() bool { - return len(r.Rules) > 0 && common.All(r.Rules, DefaultDNSRule.IsValid) + return len(r.Rules) > 0 && common.All(r.Rules, DNSRule.IsValid) } diff --git a/route/router_geo_resources.go b/route/router_geo_resources.go index 8715cf92..638d00df 100644 --- a/route/router_geo_resources.go +++ b/route/router_geo_resources.go @@ -252,10 +252,8 @@ func hasRule(rules []option.Rule, cond func(rule option.DefaultRule) bool) bool return true } case C.RuleTypeLogical: - for _, subRule := range rule.LogicalOptions.Rules { - if cond(subRule) { - return true - } + if hasRule(rule.LogicalOptions.Rules, cond) { + return true } } } @@ -270,10 +268,8 @@ func hasDNSRule(rules []option.DNSRule, cond func(rule option.DefaultDNSRule) bo return true } case C.RuleTypeLogical: - for _, subRule := range rule.LogicalOptions.Rules { - if cond(subRule) { - return true - } + if hasDNSRule(rule.LogicalOptions.Rules, cond) { + return true } } } diff --git a/route/rule_default.go b/route/rule_default.go index 2d62f97a..758aa347 100644 --- a/route/rule_default.go +++ b/route/rule_default.go @@ -220,7 +220,7 @@ func NewLogicalRule(router adapter.Router, logger log.ContextLogger, options opt return nil, E.New("unknown logical mode: ", options.Mode) } for i, subRule := range options.Rules { - rule, err := NewDefaultRule(router, logger, subRule) + rule, err := NewRule(router, logger, subRule) if err != nil { return nil, E.Cause(err, "sub rule[", i, "]") } diff --git a/route/rule_dns.go b/route/rule_dns.go index 5132f024..934c45c6 100644 --- a/route/rule_dns.go +++ b/route/rule_dns.go @@ -228,7 +228,7 @@ func NewLogicalDNSRule(router adapter.Router, logger log.ContextLogger, options return nil, E.New("unknown logical mode: ", options.Mode) } for i, subRule := range options.Rules { - rule, err := NewDefaultDNSRule(router, logger, subRule) + rule, err := NewDNSRule(router, logger, subRule) if err != nil { return nil, E.Cause(err, "sub rule[", i, "]") }