Skip to content

Commit 7fd0467

Browse files
committed
feat: sniffer's force-domain and skip-domain support rule-set: and geosite:
1 parent 696b75e commit 7fd0467

File tree

7 files changed

+166
-112
lines changed

7 files changed

+166
-112
lines changed

component/fakeip/pool.go

+4-15
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88

99
"github.com/metacubex/mihomo/common/nnip"
1010
"github.com/metacubex/mihomo/component/profile/cachefile"
11-
"github.com/metacubex/mihomo/component/trie"
1211
C "github.com/metacubex/mihomo/constant"
1312
)
1413

@@ -36,8 +35,7 @@ type Pool struct {
3635
offset netip.Addr
3736
cycle bool
3837
mux sync.Mutex
39-
host *trie.DomainTrie[struct{}]
40-
rules []C.Rule
38+
host []C.Rule
4139
ipnet netip.Prefix
4240
store store
4341
}
@@ -68,14 +66,8 @@ func (p *Pool) LookBack(ip netip.Addr) (string, bool) {
6866

6967
// ShouldSkipped return if domain should be skipped
7068
func (p *Pool) ShouldSkipped(domain string) bool {
71-
if p.host != nil {
72-
if p.host.Search(domain) != nil {
73-
return true
74-
}
75-
}
76-
for _, rule := range p.rules {
77-
metadata := &C.Metadata{Host: domain}
78-
if match, _ := rule.Match(metadata); match {
69+
for _, rule := range p.host {
70+
if match, _ := rule.Match(&C.Metadata{Host: domain}); match {
7971
return true
8072
}
8173
}
@@ -164,9 +156,7 @@ func (p *Pool) restoreState() {
164156

165157
type Options struct {
166158
IPNet netip.Prefix
167-
Host *trie.DomainTrie[struct{}]
168-
169-
Rules []C.Rule
159+
Host []C.Rule
170160

171161
// Size sets the maximum number of entries in memory
172162
// and does not work if Persistence is true
@@ -197,7 +187,6 @@ func New(options Options) (*Pool, error) {
197187
offset: first.Prev(),
198188
cycle: false,
199189
host: options.Host,
200-
rules: options.Rules,
201190
ipnet: options.IPNet,
202191
}
203192
if options.Persistence {

component/fakeip/pool_test.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import (
99

1010
"github.com/metacubex/mihomo/component/profile/cachefile"
1111
"github.com/metacubex/mihomo/component/trie"
12+
C "github.com/metacubex/mihomo/constant"
13+
RP "github.com/metacubex/mihomo/rules/provider"
1214

1315
"github.com/sagernet/bbolt"
1416
"github.com/stretchr/testify/assert"
@@ -154,7 +156,7 @@ func TestPool_Skip(t *testing.T) {
154156
pools, tempfile, err := createPools(Options{
155157
IPNet: ipnet,
156158
Size: 10,
157-
Host: tree,
159+
Host: []C.Rule{RP.NewDomainSet(tree.NewDomainSet(), "")},
158160
})
159161
assert.Nil(t, err)
160162
defer os.Remove(tempfile)

component/sniffer/dispatcher.go

+21-11
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99

1010
"github.com/metacubex/mihomo/common/lru"
1111
N "github.com/metacubex/mihomo/common/net"
12-
"github.com/metacubex/mihomo/component/trie"
1312
C "github.com/metacubex/mihomo/constant"
1413
"github.com/metacubex/mihomo/constant/sniffer"
1514
"github.com/metacubex/mihomo/log"
@@ -26,17 +25,26 @@ var Dispatcher *SnifferDispatcher
2625
type SnifferDispatcher struct {
2726
enable bool
2827
sniffers map[sniffer.Sniffer]SnifferConfig
29-
forceDomain *trie.DomainSet
30-
skipSNI *trie.DomainSet
28+
forceDomain []C.Rule
29+
skipDomain []C.Rule
3130
skipList *lru.LruCache[string, uint8]
3231
forceDnsMapping bool
3332
parsePureIp bool
3433
}
3534

3635
func (sd *SnifferDispatcher) shouldOverride(metadata *C.Metadata) bool {
37-
return (metadata.Host == "" && sd.parsePureIp) ||
38-
sd.forceDomain.Has(metadata.Host) ||
39-
(metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping)
36+
if metadata.Host == "" && sd.parsePureIp {
37+
return true
38+
}
39+
if metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping {
40+
return true
41+
}
42+
for _, rule := range sd.forceDomain {
43+
if ok, _ := rule.Match(&C.Metadata{Host: metadata.Host}); ok {
44+
return true
45+
}
46+
}
47+
return false
4048
}
4149

4250
func (sd *SnifferDispatcher) UDPSniff(packet C.PacketAdapter) bool {
@@ -94,9 +102,11 @@ func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata
94102
log.Debugln("[Sniffer] All sniffing sniff failed with from [%s:%d] to [%s:%d]", metadata.SrcIP, metadata.SrcPort, metadata.String(), metadata.DstPort)
95103
return false
96104
} else {
97-
if sd.skipSNI.Has(host) {
98-
log.Debugln("[Sniffer] Skip sni[%s]", host)
99-
return false
105+
for _, rule := range sd.skipDomain {
106+
if ok, _ := rule.Match(&C.Metadata{Host: host}); ok {
107+
log.Debugln("[Sniffer] Skip sni[%s]", host)
108+
return false
109+
}
100110
}
101111

102112
sd.skipList.Delete(dst)
@@ -187,12 +197,12 @@ func NewCloseSnifferDispatcher() (*SnifferDispatcher, error) {
187197
}
188198

189199
func NewSnifferDispatcher(snifferConfig map[sniffer.Type]SnifferConfig,
190-
forceDomain *trie.DomainSet, skipSNI *trie.DomainSet,
200+
forceDomain []C.Rule, skipDomain []C.Rule,
191201
forceDnsMapping bool, parsePureIp bool) (*SnifferDispatcher, error) {
192202
dispatcher := SnifferDispatcher{
193203
enable: true,
194204
forceDomain: forceDomain,
195-
skipSNI: skipSNI,
205+
skipDomain: skipDomain,
196206
skipList: lru.New(lru.WithSize[string, uint8](128), lru.WithAge[string, uint8](600)),
197207
forceDnsMapping: forceDnsMapping,
198208
parsePureIp: parsePureIp,

component/trie/domain.go

+7
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,13 @@ func (t *DomainTrie[T]) Foreach(fn func(domain string, data T) bool) {
134134
}
135135
}
136136

137+
func (t *DomainTrie[T]) IsEmpty() bool {
138+
if t == nil {
139+
return true
140+
}
141+
return t.root.isEmpty()
142+
}
143+
137144
func recursion[T any](items []string, node *Node[T], fn func(domain string, data T) bool) bool {
138145
for key, data := range node.getChildren() {
139146
newItems := append([]string{key}, items...)

config/config.go

+85-85
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,8 @@ type IPTables struct {
164164
type Sniffer struct {
165165
Enable bool
166166
Sniffers map[snifferTypes.Type]SNIFF.SnifferConfig
167-
ForceDomain *trie.DomainSet
168-
SkipDomain *trie.DomainSet
167+
ForceDomain []C.Rule
168+
SkipDomain []C.Rule
169169
ForceDnsMapping bool
170170
ParsePureIp bool
171171
}
@@ -627,7 +627,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
627627
}
628628
}
629629

630-
config.Sniffer, err = parseSniffer(rawCfg.Sniffer)
630+
config.Sniffer, err = parseSniffer(rawCfg.Sniffer, rules, ruleProviders)
631631
if err != nil {
632632
return nil, err
633633
}
@@ -1408,87 +1408,27 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul
14081408
return nil, err
14091409
}
14101410

1411-
var host *trie.DomainTrie[struct{}]
1412-
var fakeIPRules []C.Rule
1413-
// fake ip skip host filter
1414-
if len(cfg.FakeIPFilter) != 0 {
1415-
host = trie.New[struct{}]()
1416-
for _, domain := range cfg.FakeIPFilter {
1417-
if strings.Contains(strings.ToLower(domain), ",") {
1418-
if strings.Contains(domain, "geosite:") {
1419-
subkeys := strings.Split(domain, ":")
1420-
subkeys = subkeys[1:]
1421-
subkeys = strings.Split(subkeys[0], ",")
1422-
for _, country := range subkeys {
1423-
found := false
1424-
for _, rule := range rules {
1425-
if rule.RuleType() == C.GEOSITE {
1426-
if strings.EqualFold(country, rule.Payload()) {
1427-
found = true
1428-
fakeIPRules = append(fakeIPRules, rule)
1429-
}
1430-
}
1431-
}
1432-
if !found {
1433-
rule, err := RC.NewGEOSITE(country, "")
1434-
if err != nil {
1435-
return nil, err
1436-
}
1437-
fakeIPRules = append(fakeIPRules, rule)
1438-
}
1439-
}
1440-
1441-
}
1442-
} else if strings.Contains(strings.ToLower(domain), "rule-set:") {
1443-
subkeys := strings.Split(domain, ":")
1444-
subkeys = subkeys[1:]
1445-
subkeys = strings.Split(subkeys[0], ",")
1446-
for _, domainSetName := range subkeys {
1447-
if rp, ok := ruleProviders[domainSetName]; !ok {
1448-
return nil, fmt.Errorf("not found rule-set: %s", domainSetName)
1449-
} else {
1450-
switch rp.Behavior() {
1451-
case providerTypes.IPCIDR:
1452-
return nil, fmt.Errorf("rule provider type error, except domain,actual %s", rp.Behavior())
1453-
case providerTypes.Classical:
1454-
log.Warnln("%s provider is %s, only matching it contain domain rule", rp.Name(), rp.Behavior())
1455-
default:
1456-
}
1457-
}
1458-
rule, err := RP.NewRuleSet(domainSetName, "", true)
1459-
if err != nil {
1460-
return nil, err
1461-
}
1462-
1463-
fakeIPRules = append(fakeIPRules, rule)
1464-
}
1465-
} else {
1466-
_ = host.Insert(domain, struct{}{})
1467-
}
1468-
}
1469-
}
1470-
1411+
var fakeIPTrie *trie.DomainTrie[struct{}]
14711412
if len(dnsCfg.Fallback) != 0 {
1472-
if host == nil {
1473-
host = trie.New[struct{}]()
1474-
}
1413+
fakeIPTrie = trie.New[struct{}]()
14751414
for _, fb := range dnsCfg.Fallback {
14761415
if net.ParseIP(fb.Addr) != nil {
14771416
continue
14781417
}
1479-
_ = host.Insert(fb.Addr, struct{}{})
1418+
_ = fakeIPTrie.Insert(fb.Addr, struct{}{})
14801419
}
14811420
}
14821421

1483-
if host != nil {
1484-
host.Optimize()
1422+
// fake ip skip host filter
1423+
host, err := parseDomain(cfg.FakeIPFilter, fakeIPTrie, rules, ruleProviders)
1424+
if err != nil {
1425+
return nil, err
14851426
}
14861427

14871428
pool, err := fakeip.New(fakeip.Options{
14881429
IPNet: fakeIPRange,
14891430
Size: 1000,
14901431
Host: host,
1491-
Rules: fakeIPRules,
14921432
Persistence: rawCfg.Profile.StoreFakeIP,
14931433
})
14941434
if err != nil {
@@ -1609,7 +1549,7 @@ func parseTuicServer(rawTuic RawTuicServer, general *General) error {
16091549
return nil
16101550
}
16111551

1612-
func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) {
1552+
func parseSniffer(snifferRaw RawSniffer, rules []C.Rule, ruleProviders map[string]providerTypes.RuleProvider) (*Sniffer, error) {
16131553
sniffer := &Sniffer{
16141554
Enable: snifferRaw.Enable,
16151555
ForceDnsMapping: snifferRaw.ForceDnsMapping,
@@ -1672,23 +1612,83 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) {
16721612

16731613
sniffer.Sniffers = loadSniffer
16741614

1675-
forceDomainTrie := trie.New[struct{}]()
1676-
for _, domain := range snifferRaw.ForceDomain {
1677-
err := forceDomainTrie.Insert(domain, struct{}{})
1678-
if err != nil {
1679-
return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err)
1680-
}
1615+
forceDomain, err := parseDomain(snifferRaw.ForceDomain, nil, rules, ruleProviders)
1616+
if err != nil {
1617+
return nil, fmt.Errorf("error in force-domain, error:%w", err)
16811618
}
1682-
sniffer.ForceDomain = forceDomainTrie.NewDomainSet()
1619+
sniffer.ForceDomain = forceDomain
16831620

1684-
skipDomainTrie := trie.New[struct{}]()
1685-
for _, domain := range snifferRaw.SkipDomain {
1686-
err := skipDomainTrie.Insert(domain, struct{}{})
1687-
if err != nil {
1688-
return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err)
1689-
}
1621+
skipDomain, err := parseDomain(snifferRaw.SkipDomain, nil, rules, ruleProviders)
1622+
if err != nil {
1623+
return nil, fmt.Errorf("error in skip-domain, error:%w", err)
16901624
}
1691-
sniffer.SkipDomain = skipDomainTrie.NewDomainSet()
1625+
sniffer.SkipDomain = skipDomain
16921626

16931627
return sniffer, nil
16941628
}
1629+
1630+
func parseDomain(domains []string, domainTrie *trie.DomainTrie[struct{}], rules []C.Rule, ruleProviders map[string]providerTypes.RuleProvider) (domainRules []C.Rule, err error) {
1631+
var rule C.Rule
1632+
for _, domain := range domains {
1633+
domainLower := strings.ToLower(domain)
1634+
if strings.Contains(domainLower, "geosite:") {
1635+
subkeys := strings.Split(domain, ":")
1636+
subkeys = subkeys[1:]
1637+
subkeys = strings.Split(subkeys[0], ",")
1638+
for _, country := range subkeys {
1639+
found := false
1640+
for _, rule = range rules {
1641+
if rule.RuleType() == C.GEOSITE {
1642+
if strings.EqualFold(country, rule.Payload()) {
1643+
found = true
1644+
domainRules = append(domainRules, rule)
1645+
}
1646+
}
1647+
}
1648+
if !found {
1649+
rule, err = RC.NewGEOSITE(country, "")
1650+
if err != nil {
1651+
return nil, err
1652+
}
1653+
domainRules = append(domainRules, rule)
1654+
}
1655+
}
1656+
} else if strings.Contains(domainLower, "rule-set:") {
1657+
subkeys := strings.Split(domain, ":")
1658+
subkeys = subkeys[1:]
1659+
subkeys = strings.Split(subkeys[0], ",")
1660+
for _, domainSetName := range subkeys {
1661+
if rp, ok := ruleProviders[domainSetName]; !ok {
1662+
return nil, fmt.Errorf("not found rule-set: %s", domainSetName)
1663+
} else {
1664+
switch rp.Behavior() {
1665+
case providerTypes.IPCIDR:
1666+
return nil, fmt.Errorf("rule provider type error, except domain,actual %s", rp.Behavior())
1667+
case providerTypes.Classical:
1668+
log.Warnln("%s provider is %s, only matching it contain domain rule", rp.Name(), rp.Behavior())
1669+
default:
1670+
}
1671+
}
1672+
rule, err = RP.NewRuleSet(domainSetName, "", true)
1673+
if err != nil {
1674+
return nil, err
1675+
}
1676+
1677+
domainRules = append(domainRules, rule)
1678+
}
1679+
} else {
1680+
if domainTrie == nil {
1681+
domainTrie = trie.New[struct{}]()
1682+
}
1683+
err = domainTrie.Insert(domain, struct{}{})
1684+
if err != nil {
1685+
return nil, err
1686+
}
1687+
}
1688+
}
1689+
if !domainTrie.IsEmpty() {
1690+
rule = RP.NewDomainSet(domainTrie.NewDomainSet(), "")
1691+
domainRules = append(domainRules, rule)
1692+
}
1693+
return
1694+
}

constant/rule.go

+3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const (
2727
ProcessNameRegex
2828
ProcessPathRegex
2929
RuleSet
30+
DomainSet
3031
Network
3132
Uid
3233
SubRules
@@ -90,6 +91,8 @@ func (rt RuleType) String() string {
9091
return "Match"
9192
case RuleSet:
9293
return "RuleSet"
94+
case DomainSet:
95+
return "DomainSet"
9396
case Network:
9497
return "Network"
9598
case DSCP:

0 commit comments

Comments
 (0)