Skip to content

Commit

Permalink
Add network_[type/is_expensive/is_constrained] rule items
Browse files Browse the repository at this point in the history
  • Loading branch information
nekohasekai committed Nov 11, 2024
1 parent a8fe263 commit 4638d3c
Show file tree
Hide file tree
Showing 12 changed files with 215 additions and 32 deletions.
7 changes: 4 additions & 3 deletions cmd/sing-box/internal/convertor/adguard/convertor_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package adguard

import (
"context"
"strings"
"testing"

Expand All @@ -26,7 +27,7 @@ example.arpa
`))
require.NoError(t, err)
require.Len(t, rules, 1)
rule, err := rule.NewHeadlessRule(nil, rules[0])
rule, err := rule.NewHeadlessRule(context.Background(), rules[0])
require.NoError(t, err)
matchDomain := []string{
"example.org",
Expand Down Expand Up @@ -85,7 +86,7 @@ func TestHosts(t *testing.T) {
`))
require.NoError(t, err)
require.Len(t, rules, 1)
rule, err := rule.NewHeadlessRule(nil, rules[0])
rule, err := rule.NewHeadlessRule(context.Background(), rules[0])
require.NoError(t, err)
matchDomain := []string{
"google.com",
Expand Down Expand Up @@ -115,7 +116,7 @@ www.example.org
`))
require.NoError(t, err)
require.Len(t, rules, 1)
rule, err := rule.NewHeadlessRule(nil, rules[0])
rule, err := rule.NewHeadlessRule(context.Background(), rules[0])
require.NoError(t, err)
matchDomain := []string{
"example.com",
Expand Down
30 changes: 30 additions & 0 deletions common/srs/binary.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ const (
ruleItemWIFIBSSID
ruleItemAdGuardDomain
ruleItemProcessPathRegex
ruleItemNetworkType
ruleItemNetworkIsExpensive
ruleItemNetworkIsConstrained
ruleItemFinal uint8 = 0xFF
)

Expand Down Expand Up @@ -222,6 +225,12 @@ func readDefaultRule(reader varbin.Reader, recover bool) (rule option.DefaultHea
return
}
rule.AdGuardDomainMatcher = matcher
case ruleItemNetworkType:
rule.NetworkType, err = readRuleItemString(reader)
case ruleItemNetworkIsExpensive:
rule.NetworkIsExpensive = true
case ruleItemNetworkIsConstrained:
rule.NetworkIsConstrained = true
case ruleItemFinal:
err = binary.Read(reader, binary.BigEndian, &rule.Invert)
return
Expand Down Expand Up @@ -336,6 +345,27 @@ func writeDefaultRule(writer varbin.Writer, rule option.DefaultHeadlessRule, gen
return err
}
}
if len(rule.NetworkType) > 0 {
if generateVersion < C.RuleSetVersion3 {
return E.New("network_type rule item is only supported in version 3 or later")
}
err = writeRuleItemString(writer, ruleItemNetworkType, rule.NetworkType)
if err != nil {
return err
}
}
if rule.NetworkIsExpensive {
err = binary.Write(writer, binary.BigEndian, ruleItemNetworkIsExpensive)
if err != nil {
return err
}
}
if rule.NetworkIsConstrained {
err = binary.Write(writer, binary.BigEndian, ruleItemNetworkIsConstrained)
if err != nil {
return err
}
}
if len(rule.WIFISSID) > 0 {
err = writeRuleItemString(writer, ruleItemWIFISSID, rule.WIFISSID)
if err != nil {
Expand Down
3 changes: 2 additions & 1 deletion constant/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ const (
const (
RuleSetVersion1 = 1 + iota
RuleSetVersion2
RuleSetVersionCurrent = RuleSetVersion2
RuleSetVersion3
RuleSetVersionCurrent = RuleSetVersion3
)

const (
Expand Down
3 changes: 3 additions & 0 deletions option/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ type RawDefaultRule struct {
User badoption.Listable[string] `json:"user,omitempty"`
UserID badoption.Listable[int32] `json:"user_id,omitempty"`
ClashMode string `json:"clash_mode,omitempty"`
NetworkType badoption.Listable[string] `json:"network_type,omitempty"`
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"`
Expand Down
3 changes: 3 additions & 0 deletions option/rule_dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ type RawDefaultDNSRule struct {
UserID badoption.Listable[int32] `json:"user_id,omitempty"`
Outbound badoption.Listable[string] `json:"outbound,omitempty"`
ClashMode string `json:"clash_mode,omitempty"`
NetworkType badoption.Listable[string] `json:"network_type,omitempty"`
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
RuleSet badoption.Listable[string] `json:"rule_set,omitempty"`
Expand Down
49 changes: 26 additions & 23 deletions option/rule_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,25 +146,28 @@ func (r HeadlessRule) IsValid() bool {
}

type DefaultHeadlessRule struct {
QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"`
Network badoption.Listable[string] `json:"network,omitempty"`
Domain badoption.Listable[string] `json:"domain,omitempty"`
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
Port badoption.Listable[uint16] `json:"port,omitempty"`
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
PackageName badoption.Listable[string] `json:"package_name,omitempty"`
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
Invert bool `json:"invert,omitempty"`
QueryType badoption.Listable[DNSQueryType] `json:"query_type,omitempty"`
Network badoption.Listable[string] `json:"network,omitempty"`
Domain badoption.Listable[string] `json:"domain,omitempty"`
DomainSuffix badoption.Listable[string] `json:"domain_suffix,omitempty"`
DomainKeyword badoption.Listable[string] `json:"domain_keyword,omitempty"`
DomainRegex badoption.Listable[string] `json:"domain_regex,omitempty"`
SourceIPCIDR badoption.Listable[string] `json:"source_ip_cidr,omitempty"`
IPCIDR badoption.Listable[string] `json:"ip_cidr,omitempty"`
SourcePort badoption.Listable[uint16] `json:"source_port,omitempty"`
SourcePortRange badoption.Listable[string] `json:"source_port_range,omitempty"`
Port badoption.Listable[uint16] `json:"port,omitempty"`
PortRange badoption.Listable[string] `json:"port_range,omitempty"`
ProcessName badoption.Listable[string] `json:"process_name,omitempty"`
ProcessPath badoption.Listable[string] `json:"process_path,omitempty"`
ProcessPathRegex badoption.Listable[string] `json:"process_path_regex,omitempty"`
PackageName badoption.Listable[string] `json:"package_name,omitempty"`
NetworkType badoption.Listable[string] `json:"network_type,omitempty"`
NetworkIsExpensive bool `json:"network_is_expensive,omitempty"`
NetworkIsConstrained bool `json:"network_is_constrained,omitempty"`
WIFISSID badoption.Listable[string] `json:"wifi_ssid,omitempty"`
WIFIBSSID badoption.Listable[string] `json:"wifi_bssid,omitempty"`
Invert bool `json:"invert,omitempty"`

DomainMatcher *domain.Matcher `json:"-"`
SourceIPSet *netipx.IPSet `json:"-"`
Expand All @@ -191,7 +194,7 @@ func (r LogicalHeadlessRule) IsValid() bool {
}

type _PlainRuleSetCompat struct {
Version int `json:"version"`
Version uint8 `json:"version"`
Options PlainRuleSet `json:"-"`
}

Expand All @@ -200,7 +203,7 @@ type PlainRuleSetCompat _PlainRuleSetCompat
func (r PlainRuleSetCompat) MarshalJSON() ([]byte, error) {
var v any
switch r.Version {
case C.RuleSetVersion1, C.RuleSetVersion2:
case C.RuleSetVersion1, C.RuleSetVersion2, C.RuleSetVersion3:
v = r.Options
default:
return nil, E.New("unknown rule-set version: ", r.Version)
Expand All @@ -215,7 +218,7 @@ func (r *PlainRuleSetCompat) UnmarshalJSON(bytes []byte) error {
}
var v any
switch r.Version {
case C.RuleSetVersion1, C.RuleSetVersion2:
case C.RuleSetVersion1, C.RuleSetVersion2, C.RuleSetVersion3:
v = &r.Options
case 0:
return E.New("missing rule-set version")
Expand All @@ -231,7 +234,7 @@ func (r *PlainRuleSetCompat) UnmarshalJSON(bytes []byte) error {

func (r PlainRuleSetCompat) Upgrade() (PlainRuleSet, error) {
switch r.Version {
case C.RuleSetVersion1, C.RuleSetVersion2:
case C.RuleSetVersion1, C.RuleSetVersion2, C.RuleSetVersion3:
default:
return PlainRuleSet{}, E.New("unknown rule-set version: " + F.ToString(r.Version))
}
Expand Down
15 changes: 15 additions & 0 deletions route/rule/rule_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,21 @@ func NewDefaultRule(ctx context.Context, logger log.ContextLogger, options optio
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}
if len(options.NetworkType) > 0 {
item := NewNetworkTypeItem(networkManager, options.NetworkType)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}
if options.NetworkIsExpensive {
item := NewNetworkIsExpensiveItem(networkManager)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}
if options.NetworkIsConstrained {
item := NewNetworkIsConstrainedItem(networkManager)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}
if len(options.WIFISSID) > 0 {
item := NewWIFISSIDItem(networkManager, options.WIFISSID)
rule.items = append(rule.items, item)
Expand Down
15 changes: 15 additions & 0 deletions route/rule/rule_dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,21 @@ func NewDefaultDNSRule(ctx context.Context, logger log.ContextLogger, options op
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}
if len(options.NetworkType) > 0 {
item := NewNetworkTypeItem(networkManager, options.NetworkType)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}
if options.NetworkIsExpensive {
item := NewNetworkIsExpensiveItem(networkManager)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}
if options.NetworkIsConstrained {
item := NewNetworkIsConstrainedItem(networkManager)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}
if len(options.WIFISSID) > 0 {
item := NewWIFISSIDItem(networkManager, options.WIFISSID)
rule.items = append(rule.items, item)
Expand Down
25 changes: 20 additions & 5 deletions route/rule/rule_headless.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,18 +140,33 @@ func NewDefaultHeadlessRule(ctx context.Context, options option.DefaultHeadlessR
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}
if len(options.WIFISSID) > 0 {
if networkManager != nil {
if networkManager != nil {
if len(options.NetworkType) > 0 {
item := NewNetworkTypeItem(networkManager, options.NetworkType)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}
if options.NetworkIsExpensive {
item := NewNetworkIsExpensiveItem(networkManager)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}
if options.NetworkIsConstrained {
item := NewNetworkIsConstrainedItem(networkManager)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)
}
if len(options.WIFISSID) > 0 {
item := NewWIFISSIDItem(networkManager, options.WIFISSID)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)

}
}
if len(options.WIFIBSSID) > 0 {
if networkManager != nil {
if len(options.WIFIBSSID) > 0 {
item := NewWIFIBSSIDItem(networkManager, options.WIFIBSSID)
rule.items = append(rule.items, item)
rule.allItems = append(rule.allItems, item)

}
}
if len(options.AdGuardDomain) > 0 {
Expand Down
29 changes: 29 additions & 0 deletions route/rule/rule_item_network_is_constrained.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package rule

import (
"github.com/sagernet/sing-box/adapter"
)

var _ RuleItem = (*NetworkIsConstrainedItem)(nil)

type NetworkIsConstrainedItem struct {
networkManager adapter.NetworkManager
}

func NewNetworkIsConstrainedItem(networkManager adapter.NetworkManager) *NetworkIsConstrainedItem {
return &NetworkIsConstrainedItem{
networkManager: networkManager,
}
}

func (r *NetworkIsConstrainedItem) Match(metadata *adapter.InboundContext) bool {
networkInterface := r.networkManager.DefaultNetworkInterface()
if networkInterface == nil {
return false
}
return networkInterface.Constrained
}

func (r *NetworkIsConstrainedItem) String() string {
return "network_is_expensive=true"
}
29 changes: 29 additions & 0 deletions route/rule/rule_item_network_is_expensive.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package rule

import (
"github.com/sagernet/sing-box/adapter"
)

var _ RuleItem = (*NetworkIsExpensiveItem)(nil)

type NetworkIsExpensiveItem struct {
networkManager adapter.NetworkManager
}

func NewNetworkIsExpensiveItem(networkManager adapter.NetworkManager) *NetworkIsExpensiveItem {
return &NetworkIsExpensiveItem{
networkManager: networkManager,
}
}

func (r *NetworkIsExpensiveItem) Match(metadata *adapter.InboundContext) bool {
networkInterface := r.networkManager.DefaultNetworkInterface()
if networkInterface == nil {
return false
}
return networkInterface.Expensive
}

func (r *NetworkIsExpensiveItem) String() string {
return "network_is_expensive=true"
}
39 changes: 39 additions & 0 deletions route/rule/rule_item_network_type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package rule

import (
"strings"

"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/sing/common"
F "github.com/sagernet/sing/common/format"
)

var _ RuleItem = (*NetworkTypeItem)(nil)

type NetworkTypeItem struct {
networkManager adapter.NetworkManager
networkType []string
}

func NewNetworkTypeItem(networkManager adapter.NetworkManager, networkType []string) *NetworkTypeItem {
return &NetworkTypeItem{
networkManager: networkManager,
networkType: networkType,
}
}

func (r *NetworkTypeItem) Match(metadata *adapter.InboundContext) bool {
networkInterface := r.networkManager.DefaultNetworkInterface()
if networkInterface == nil {
return false
}
return common.Contains(r.networkType, networkInterface.Type)
}

func (r *NetworkTypeItem) String() string {
if len(r.networkType) == 1 {
return F.ToString("network_type=", r.networkType[0])
} else {
return F.ToString("network_type=", "["+strings.Join(F.MapToString(r.networkType), " ")+"]")
}
}

0 comments on commit 4638d3c

Please sign in to comment.