Compare commits

..

37 Commits

Author SHA1 Message Date
9b7aab1fc7 chore: rename delete.yml 2023-03-30 16:04:09 +00:00
3c717097cb Merge branch 'Alpha' into Meta 2023-03-30 16:03:47 +00:00
991de009be chore: update readme 2023-03-30 15:57:52 +00:00
2fef329319 fix: upgrade backup 2023-03-29 13:59:36 +00:00
db7623968d fix: inner http use host of address 2023-03-29 20:50:46 +08:00
7c80c88feb chore: push latest alpha core to MetaCubeX/AlphaBinary 2023-03-29 12:28:16 +00:00
2c7153cd7a chore: clean up code 2023-03-29 16:19:26 +08:00
d730feecb4 chore: use inner for upgrade core 2023-03-29 06:03:13 +00:00
8293b7fdae Merge branch 'Beta' into Meta
# Conflicts:
#	.github/workflows/docker.yaml
#	.github/workflows/prerelease.yml
2023-02-19 01:25:34 +08:00
0ba415866e Merge branch 'Alpha' into Beta 2023-02-19 01:25:01 +08:00
53b41ca166 Chore: Add action for deleting old workflow 2023-01-30 18:17:22 +08:00
8a75f78e63 chore: adjust Dockerfile 2023-01-12 02:24:12 +08:00
d9692c6366 Merge branch 'Beta' into Meta 2023-01-12 02:15:14 +08:00
f4b0062dfc Merge branch 'Alpha' into Beta 2023-01-12 02:14:49 +08:00
b9ffc82e53 Merge branch 'Beta' into Meta 2023-01-12 01:33:56 +08:00
78aaea6a45 Merge branch 'Alpha' into Beta 2023-01-12 01:33:16 +08:00
3645fbf161 Merge pull request #327 from Rasphino/Meta
Update flake.nix hash
2023-01-08 00:29:29 +08:00
a1d0f22132 fix: update flake.nix hash 2023-01-07 23:38:32 +08:00
fa73b0f4bf Merge remote-tracking branch 'origin/Beta' into Meta 2023-01-01 19:41:36 +08:00
3b76a8b839 Merge remote-tracking branch 'origin/Alpha' into Beta 2023-01-01 19:40:36 +08:00
667f42dcdc Merge pull request #282 from tdjnodj/Meta
Update README.md
2022-12-03 17:23:51 +08:00
dfbe09860f Update README.md 2022-12-03 17:17:08 +08:00
9e20f9c26a chore: update dependencies 2022-11-28 20:33:10 +08:00
f968d0cb82 chore: update github action 2022-11-26 20:16:12 +08:00
2ad84f4379 Merge branch 'Beta' into Meta 2022-11-02 18:08:22 +08:00
c7aa16426f Merge branch 'Alpha' into Beta 2022-11-02 18:07:29 +08:00
5987f8e3b5 Merge branch 'Beta' into Meta 2022-08-29 13:08:29 +08:00
3a8eb72de2 Merge branch 'Alpha' into Beta 2022-08-29 13:08:22 +08:00
33abbdfd24 Merge pull request #174 from MetaCubeX/Alpha
Alpha
2022-08-29 11:24:07 +08:00
0703d6cbff Merge pull request #173 from MetaCubeX/Alpha
Alpha
2022-08-29 11:22:14 +08:00
10d2d14938 Merge branch 'Beta' into Meta
# Conflicts:
#	rules/provider/classical_strategy.go
2022-07-02 10:41:41 +08:00
691cf1d8d6 Merge pull request #94 from bash99/Meta
Update README.md
2022-06-15 19:15:51 +08:00
d1decb8e58 Update README.md
add permissions for systemctl services
clash-dashboard change to updated one
2022-06-15 14:00:05 +08:00
7d04904109 fix: leak dns when domain in hosts list 2022-06-11 18:51:26 +08:00
a5acd3aa97 refactor: clear linkname,reduce cycle dependencies,transport init geosite function 2022-06-11 18:51:22 +08:00
eea9a12560 fix: 规则匹配默认策略组返回错误 2022-06-09 14:18:35 +08:00
0a4570b55c fix: group filter touch provider 2022-06-09 14:18:29 +08:00
40 changed files with 221 additions and 593 deletions

16
.github/workflows/Delete.yml vendored Normal file
View File

@ -0,0 +1,16 @@
name: Delete old workflow runs
on:
schedule:
- cron: '0 0 1 * *'
# Run monthly, at 00:00 on the 1st day of month.
jobs:
del_runs:
runs-on: ubuntu-latest
steps:
- name: Delete workflow runs
uses: GitRML/delete-workflow-runs@main
with:
token: ${{ secrets.AUTH_PAT }}
repository: ${{ github.repository }}
retain_days: 30

View File

@ -268,18 +268,6 @@ jobs:
generate_release_notes: true generate_release_notes: true
body_path: release.txt body_path: release.txt
- name: Git push assets to "release" branch
run: |
cd bin || exit 1
git init
git config --local user.name "github-actions[bot]"
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
git checkout -b release
git add .
git commit -m "${{ env.BUILDTIME }}"
git remote add origin "https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}"
git push -f -u origin release
Upload-Release: Upload-Release:
permissions: write-all permissions: write-all
if: ${{ github.ref_type=='tag' }} if: ${{ github.ref_type=='tag' }}

16
.github/workflows/delete.yml vendored Normal file
View File

@ -0,0 +1,16 @@
name: Delete old workflow runs
on:
schedule:
- cron: '0 0 1 * *'
# Run monthly, at 00:00 on the 1st day of month.
jobs:
del_runs:
runs-on: ubuntu-latest
steps:
- name: Delete workflow runs
uses: GitRML/delete-workflow-runs@main
with:
token: ${{ secrets.AUTH_PAT }}
repository: ${{ github.repository }}
retain_days: 30

View File

@ -30,8 +30,7 @@
- Comprehensive HTTP RESTful API controller - Comprehensive HTTP RESTful API controller
## Wiki ## Wiki
Configuration examples can be found at [/docs/config.yaml](https://github.com/MetaCubeX/Clash.Meta/blob/Alpha/docs/config.yaml), while documentation can be found [Clash.Meta Wiki](https://clash-meta.wiki).
Documentation and configuring examples are available on [Clash.Meta Wiki](https://clash-meta.wiki).
## Build ## Build

View File

@ -42,6 +42,8 @@ func NewInner(conn net.Conn, dst string, host string) *context.ConnContext {
if host == "" { if host == "" {
if ip, err := netip.ParseAddr(h); err == nil { if ip, err := netip.ParseAddr(h); err == nil {
metadata.DstIP = ip metadata.DstIP = ip
} else {
metadata.Host = h
} }
} }
} }

View File

@ -1,15 +0,0 @@
//go:build with_low_memory
package pool
const (
// io.Copy default buffer size is 32 KiB
// but the maximum packet size of vmess/shadowsocks is about 16 KiB
// so define a buffer of 20 KiB to reduce the memory of each TCP relay
RelayBufferSize = 16 * 1024
// RelayBufferSize uses 20KiB, but due to the allocator it will actually
// request 32Kib. Most UDPs are smaller than the MTU, and the TUN's MTU
// set to 9000, so the UDP Buffer size set to 16Kib
UDPBufferSize = 8 * 1024
)

View File

@ -1,15 +0,0 @@
//go:build !with_low_memory
package pool
const (
// io.Copy default buffer size is 32 KiB
// but the maximum packet size of vmess/shadowsocks is about 16 KiB
// so define a buffer of 20 KiB to reduce the memory of each TCP relay
RelayBufferSize = 20 * 1024
// RelayBufferSize uses 20KiB, but due to the allocator it will actually
// request 32Kib. Most UDPs are smaller than the MTU, and the TUN's MTU
// set to 9000, so the UDP Buffer size set to 16Kib
UDPBufferSize = 16 * 1024
)

View File

@ -1,5 +1,17 @@
package pool package pool
const (
// io.Copy default buffer size is 32 KiB
// but the maximum packet size of vmess/shadowsocks is about 16 KiB
// so define a buffer of 20 KiB to reduce the memory of each TCP relay
RelayBufferSize = 20 * 1024
// RelayBufferSize uses 20KiB, but due to the allocator it will actually
// request 32Kib. Most UDPs are smaller than the MTU, and the TUN's MTU
// set to 9000, so the UDP Buffer size set to 16Kib
UDPBufferSize = 16 * 1024
)
func Get(size int) []byte { func Get(size int) []byte {
return defaultAllocator.Get(size) return defaultAllocator.Get(size)
} }

View File

@ -1,9 +0,0 @@
package utils
func Reverse(s string) string {
a := []rune(s)
for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
a[i], a[j] = a[j], a[i]
}
return string(a)
}

View File

@ -53,7 +53,7 @@ func HttpRequest(ctx context.Context, url, method string, header map[string][]st
TLSHandshakeTimeout: 10 * time.Second, TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second, ExpectContinueTimeout: 1 * time.Second,
DialContext: func(ctx context.Context, network, address string) (net.Conn, error) { DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
conn := inner.HandleTcp(address, urlRes.Hostname()) conn := inner.HandleTcp(address, "")
return conn, nil return conn, nil
}, },
TLSClientConfig: tls.GetDefaultTLSConfig(), TLSClientConfig: tls.GetDefaultTLSConfig(),

View File

@ -28,8 +28,8 @@ var Dispatcher *SnifferDispatcher
type SnifferDispatcher struct { type SnifferDispatcher struct {
enable bool enable bool
sniffers map[sniffer.Sniffer]SnifferConfig sniffers map[sniffer.Sniffer]SnifferConfig
forceDomain *trie.DomainSet forceDomain *trie.DomainTrie[struct{}]
skipSNI *trie.DomainSet skipSNI *trie.DomainTrie[struct{}]
skipList *cache.LruCache[string, uint8] skipList *cache.LruCache[string, uint8]
rwMux sync.RWMutex rwMux sync.RWMutex
forceDnsMapping bool forceDnsMapping bool
@ -37,7 +37,7 @@ type SnifferDispatcher struct {
} }
func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) { func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) {
if (metadata.Host == "" && sd.parsePureIp) || sd.forceDomain.Has(metadata.Host) || (metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping) { if (metadata.Host == "" && sd.parsePureIp) || sd.forceDomain.Search(metadata.Host) != nil || (metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping) {
port, err := strconv.ParseUint(metadata.DstPort, 10, 16) port, err := strconv.ParseUint(metadata.DstPort, 10, 16)
if err != nil { if err != nil {
log.Debugln("[Sniffer] Dst port is error") log.Debugln("[Sniffer] Dst port is error")
@ -74,7 +74,7 @@ func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata
log.Debugln("[Sniffer] All sniffing sniff failed with from [%s:%s] to [%s:%s]", metadata.SrcIP, metadata.SrcPort, metadata.String(), metadata.DstPort) log.Debugln("[Sniffer] All sniffing sniff failed with from [%s:%s] to [%s:%s]", metadata.SrcIP, metadata.SrcPort, metadata.String(), metadata.DstPort)
return return
} else { } else {
if sd.skipSNI.Has(host) { if sd.skipSNI.Search(host) != nil {
log.Debugln("[Sniffer] Skip sni[%s]", host) log.Debugln("[Sniffer] Skip sni[%s]", host)
return return
} }
@ -166,8 +166,8 @@ func NewCloseSnifferDispatcher() (*SnifferDispatcher, error) {
return &dispatcher, nil return &dispatcher, nil
} }
func NewSnifferDispatcher(snifferConfig map[sniffer.Type]SnifferConfig, func NewSnifferDispatcher(snifferConfig map[sniffer.Type]SnifferConfig, forceDomain *trie.DomainTrie[struct{}],
forceDomain *trie.DomainSet, skipSNI *trie.DomainSet, skipSNI *trie.DomainTrie[struct{}],
forceDnsMapping bool, parsePureIp bool) (*SnifferDispatcher, error) { forceDnsMapping bool, parsePureIp bool) (*SnifferDispatcher, error) {
dispatcher := SnifferDispatcher{ dispatcher := SnifferDispatcher{
enable: true, enable: true,

View File

@ -15,7 +15,7 @@ import (
) )
var trustCerts []*x509.Certificate var trustCerts []*x509.Certificate
var certPool *x509.CertPool
var mutex sync.RWMutex var mutex sync.RWMutex
var errNotMacth error = errors.New("certificate fingerprints do not match") var errNotMacth error = errors.New("certificate fingerprints do not match")
@ -40,20 +40,10 @@ func ResetCertificate() {
} }
func getCertPool() *x509.CertPool { func getCertPool() *x509.CertPool {
if len(trustCerts) == 0 { certPool, err := x509.SystemCertPool()
return nil if err == nil {
} for _, cert := range trustCerts {
if certPool == nil { certPool.AddCert(cert)
mutex.Lock()
defer mutex.Unlock()
if certPool != nil {
return certPool
}
certPool, err := x509.SystemCertPool()
if err == nil {
for _, cert := range trustCerts {
certPool.AddCert(cert)
}
} }
} }
return certPool return certPool

View File

@ -25,7 +25,7 @@ func ValidAndSplitDomain(domain string) ([]string, bool) {
if domain != "" && domain[len(domain)-1] == '.' { if domain != "" && domain[len(domain)-1] == '.' {
return nil, false return nil, false
} }
domain=strings.ToLower(domain)
parts := strings.Split(domain, domainStep) parts := strings.Split(domain, domainStep)
if len(parts) == 1 { if len(parts) == 1 {
if parts[0] == "" { if parts[0] == "" {
@ -123,30 +123,6 @@ func (t *DomainTrie[T]) Optimize() {
t.root.optimize() t.root.optimize()
} }
func (t *DomainTrie[T]) Foreach(print func(domain string, data T)) {
for key, data := range t.root.getChildren() {
recursion([]string{key}, data, print)
}
}
func recursion[T any](items []string, node *Node[T], fn func(domain string, data T)) {
for key, data := range node.getChildren() {
newItems := append([]string{key}, items...)
if data != nil && data.inited {
domain := joinDomain(newItems)
if domain[0] == domainStepByte {
domain = complexWildcard + domain
}
fn(domain, data.Data())
}
recursion(newItems, data, fn)
}
}
func joinDomain(items []string) string {
return strings.Join(items, domainStep)
}
// New returns a new, empty Trie. // New returns a new, empty Trie.
func New[T any]() *DomainTrie[T] { func New[T any]() *DomainTrie[T] {
return &DomainTrie[T]{root: newNode[T]()} return &DomainTrie[T]{root: newNode[T]()}

View File

@ -105,23 +105,3 @@ func TestTrie_WildcardBoundary(t *testing.T) {
assert.NotNil(t, tree.Search("example.com")) assert.NotNil(t, tree.Search("example.com"))
} }
func TestTrie_Foreach(t *testing.T) {
tree := New[netip.Addr]()
domainList := []string{
"google.com",
"stun.*.*.*",
"test.*.google.com",
"+.baidu.com",
"*.baidu.com",
"*.*.baidu.com",
}
for _, domain := range domainList {
tree.Insert(domain, localIP)
}
count := 0
tree.Foreach(func(domain string, data netip.Addr) {
count++
})
assert.Equal(t, 7, count)
}

View File

@ -116,18 +116,6 @@ func (n *Node[T]) setData(data T) {
n.inited = true n.inited = true
} }
func (n *Node[T]) getChildren() map[string]*Node[T] {
if n.childMap == nil {
if n.childNode != nil {
m := make(map[string]*Node[T])
m[n.childStr] = n.childNode
return m
}
} else {
return n.childMap
}
return nil
}
func (n *Node[T]) Data() T { func (n *Node[T]) Data() T {
return n.data return n.data
} }

View File

@ -1,60 +0,0 @@
package trie_test
import (
"testing"
"github.com/Dreamacro/clash/component/trie"
"github.com/stretchr/testify/assert"
)
func TestDomain(t *testing.T) {
domainSet := []string{
"baidu.com",
"google.com",
"www.google.com",
"test.a.net",
"test.a.oc",
}
set := trie.NewDomainSet(domainSet)
assert.NotNil(t, set)
assert.True(t, set.Has("test.a.net"))
assert.True(t, set.Has("google.com"))
assert.False(t, set.Has("www.baidu.com"))
}
func TestDomainComplexWildcard(t *testing.T) {
domainSet := []string{
"+.baidu.com",
"+.a.baidu.com",
"www.baidu.com",
"+.bb.baidu.com",
"test.a.net",
"test.a.oc",
"www.qq.com",
}
set := trie.NewDomainSet(domainSet)
assert.NotNil(t, set)
assert.False(t, set.Has("google.com"))
assert.True(t, set.Has("www.baidu.com"))
assert.True(t, set.Has("test.test.baidu.com"))
}
func TestDomainWildcard(t *testing.T) {
domainSet := []string{
"*.*.*.baidu.com",
"www.baidu.*",
"stun.*.*",
"*.*.qq.com",
"test.*.baidu.com",
}
set := trie.NewDomainSet(domainSet)
assert.NotNil(t, set)
assert.True(t, set.Has("www.baidu.com"))
assert.True(t, set.Has("test.test.baidu.com"))
assert.True(t, set.Has("test.test.qq.com"))
assert.True(t,set.Has("stun.ab.cd"))
assert.False(t, set.Has("test.baidu.com"))
assert.False(t,set.Has("www.google.com"))
assert.False(t, set.Has("test.qq.com"))
assert.False(t, set.Has("test.test.test.qq.com"))
}

View File

@ -1,178 +0,0 @@
package trie
// Package succinct provides several succinct data types.
// Modify from https://github.com/openacid/succinct/blob/d4684c35d123f7528b14e03c24327231723db704/sskv.go
import (
"sort"
"strings"
"github.com/Dreamacro/clash/common/utils"
"github.com/openacid/low/bitmap"
)
const (
complexWildcardByte = byte('+')
wildcardByte = byte('*')
domainStepByte = byte('.')
)
type DomainSet struct {
leaves, labelBitmap []uint64
labels []byte
ranks, selects []int32
}
// NewDomainSet creates a new *DomainSet struct, from a slice of sorted strings.
func NewDomainSet(keys []string) *DomainSet {
domainTrie := New[struct{}]()
for _, domain := range keys {
domainTrie.Insert(domain, struct{}{})
}
reserveDomains := make([]string, 0, len(keys))
domainTrie.Foreach(func(domain string, data struct{}) {
reserveDomains = append(reserveDomains, utils.Reverse(domain))
})
// ensure that the same prefix is continuous
// and according to the ascending sequence of length
sort.Strings(reserveDomains)
keys = reserveDomains
if len(keys) == 0 {
return nil
}
ss := &DomainSet{}
lIdx := 0
type qElt struct{ s, e, col int }
queue := []qElt{{0, len(keys), 0}}
for i := 0; i < len(queue); i++ {
elt := queue[i]
if elt.col == len(keys[elt.s]) {
elt.s++
// a leaf node
setBit(&ss.leaves, i, 1)
}
for j := elt.s; j < elt.e; {
frm := j
for ; j < elt.e && keys[j][elt.col] == keys[frm][elt.col]; j++ {
}
queue = append(queue, qElt{frm, j, elt.col + 1})
ss.labels = append(ss.labels, keys[frm][elt.col])
setBit(&ss.labelBitmap, lIdx, 0)
lIdx++
}
setBit(&ss.labelBitmap, lIdx, 1)
lIdx++
}
ss.init()
return ss
}
// Has query for a key and return whether it presents in the DomainSet.
func (ss *DomainSet) Has(key string) bool {
if ss == nil {
return false
}
key = utils.Reverse(key)
key = strings.ToLower(key)
// no more labels in this node
// skip character matching
// go to next level
nodeId, bmIdx := 0, 0
type wildcardCursor struct {
bmIdx, index int
}
stack := make([]wildcardCursor, 0)
for i := 0; i < len(key); i++ {
RESTART:
c := key[i]
for ; ; bmIdx++ {
if getBit(ss.labelBitmap, bmIdx) != 0 {
if len(stack) > 0 {
cursor := stack[len(stack)-1]
stack = stack[0 : len(stack)-1]
// back wildcard and find next node
nextNodeId := countZeros(ss.labelBitmap, ss.ranks, cursor.bmIdx+1)
nextBmIdx := selectIthOne(ss.labelBitmap, ss.ranks, ss.selects, nextNodeId-1) + 1
j := cursor.index
for ; j < len(key) && key[j] != domainStepByte; j++ {
}
if j == len(key) {
if getBit(ss.leaves, nextNodeId) != 0 {
return true
}else {
goto RESTART
}
}
for ; ; nextBmIdx++ {
if ss.labels[nextBmIdx-nextNodeId] == domainStepByte {
bmIdx = nextBmIdx
nodeId = nextNodeId
i = j
goto RESTART
}
}
}
return false
}
// handle wildcard for domain
if ss.labels[bmIdx-nodeId] == complexWildcardByte {
return true
} else if ss.labels[bmIdx-nodeId] == wildcardByte {
cursor := wildcardCursor{}
cursor.bmIdx = bmIdx
cursor.index = i
stack = append(stack, cursor)
} else if ss.labels[bmIdx-nodeId] == c {
break
}
}
nodeId = countZeros(ss.labelBitmap, ss.ranks, bmIdx+1)
bmIdx = selectIthOne(ss.labelBitmap, ss.ranks, ss.selects, nodeId-1) + 1
}
return getBit(ss.leaves, nodeId) != 0
}
func setBit(bm *[]uint64, i int, v int) {
for i>>6 >= len(*bm) {
*bm = append(*bm, 0)
}
(*bm)[i>>6] |= uint64(v) << uint(i&63)
}
func getBit(bm []uint64, i int) uint64 {
return bm[i>>6] & (1 << uint(i&63))
}
// init builds pre-calculated cache to speed up rank() and select()
func (ss *DomainSet) init() {
ss.selects, ss.ranks = bitmap.IndexSelect32R64(ss.labelBitmap)
}
// countZeros counts the number of "0" in a bitmap before the i-th bit(excluding
// the i-th bit) on behalf of rank index.
// E.g.:
//
// countZeros("010010", 4) == 3
// // 012345
func countZeros(bm []uint64, ranks []int32, i int) int {
a, _ := bitmap.Rank64(bm, ranks, int32(i))
return i - int(a)
}
// selectIthOne returns the index of the i-th "1" in a bitmap, on behalf of rank
// and select indexes.
// E.g.:
//
// selectIthOne("010010", 1) == 4
// // 012345
func selectIthOne(bm []uint64, ranks, selects []int32, i int) int {
a, _ := bitmap.Select32R64(bm, selects, ranks, int32(i))
return int(a)
}

View File

@ -9,6 +9,7 @@ import (
"net/url" "net/url"
"os" "os"
"regexp" "regexp"
"runtime"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -135,8 +136,9 @@ type IPTables struct {
type Sniffer struct { type Sniffer struct {
Enable bool Enable bool
Sniffers map[snifferTypes.Type]SNIFF.SnifferConfig Sniffers map[snifferTypes.Type]SNIFF.SnifferConfig
ForceDomain *trie.DomainSet Reverses *trie.DomainTrie[struct{}]
SkipDomain *trie.DomainSet ForceDomain *trie.DomainTrie[struct{}]
SkipDomain *trie.DomainTrie[struct{}]
ForceDnsMapping bool ForceDnsMapping bool
ParsePureIp bool ParsePureIp bool
} }
@ -488,7 +490,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
} }
config.Hosts = hosts config.Hosts = hosts
dnsCfg, err := parseDNS(rawCfg, hosts, rules, ruleProviders) dnsCfg, err := parseDNS(rawCfg, hosts, rules)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -820,6 +822,8 @@ func parseRules(rulesConfig []string, proxies map[string]C.Proxy, subRules map[s
rules = append(rules, parsed) rules = append(rules, parsed)
} }
runtime.GC()
return rules, nil return rules, nil
} }
@ -979,7 +983,7 @@ func parsePureDNSServer(server string) string {
} }
} }
} }
func parseNameServerPolicy(nsPolicy map[string]any, ruleProviders map[string]providerTypes.RuleProvider, preferH3 bool) (map[string][]dns.NameServer, error) { func parseNameServerPolicy(nsPolicy map[string]any, preferH3 bool) (map[string][]dns.NameServer, error) {
policy := map[string][]dns.NameServer{} policy := map[string][]dns.NameServer{}
updatedPolicy := make(map[string]interface{}) updatedPolicy := make(map[string]interface{})
re := regexp.MustCompile(`[a-zA-Z0-9\-]+\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?`) re := regexp.MustCompile(`[a-zA-Z0-9\-]+\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?`)
@ -994,14 +998,6 @@ func parseNameServerPolicy(nsPolicy map[string]any, ruleProviders map[string]pro
newKey := "geosite:" + subkey newKey := "geosite:" + subkey
updatedPolicy[newKey] = v updatedPolicy[newKey] = v
} }
} else if strings.Contains(k, "rule-set:") {
subkeys := strings.Split(k, ":")
subkeys = subkeys[1:]
subkeys = strings.Split(subkeys[0], ",")
for _, subkey := range subkeys {
newKey := "rule-set:" + subkey
updatedPolicy[newKey] = v
}
} else if re.MatchString(k) { } else if re.MatchString(k) {
subkeys := strings.Split(k, ",") subkeys := strings.Split(k, ",")
for _, subkey := range subkeys { for _, subkey := range subkeys {
@ -1025,19 +1021,6 @@ func parseNameServerPolicy(nsPolicy map[string]any, ruleProviders map[string]pro
if _, valid := trie.ValidAndSplitDomain(domain); !valid { if _, valid := trie.ValidAndSplitDomain(domain); !valid {
return nil, fmt.Errorf("DNS ResoverRule invalid domain: %s", domain) return nil, fmt.Errorf("DNS ResoverRule invalid domain: %s", domain)
} }
if strings.HasPrefix(domain, "rule-set:") {
domainSetName := domain[9:]
if provider, ok := ruleProviders[domainSetName]; !ok {
return nil, fmt.Errorf("not found rule-set: %s", domainSetName)
} else {
switch provider.Behavior() {
case providerTypes.IPCIDR:
return nil, fmt.Errorf("rule provider type error, except domain,actual %s", provider.Behavior())
case providerTypes.Classical:
log.Warnln("%s provider is %s, only matching it contain domain rule", provider.Name(), provider.Behavior())
}
}
}
policy[domain] = nameservers policy[domain] = nameservers
} }
@ -1090,10 +1073,11 @@ func parseFallbackGeoSite(countries []string, rules []C.Rule) ([]*router.DomainM
log.Infoln("Start initial GeoSite dns fallback filter `%s`, records: %d", country, recordsCount) log.Infoln("Start initial GeoSite dns fallback filter `%s`, records: %d", country, recordsCount)
} }
} }
runtime.GC()
return sites, nil return sites, nil
} }
func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rules []C.Rule, ruleProviders map[string]providerTypes.RuleProvider) (*DNS, error) { func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rules []C.Rule) (*DNS, error) {
cfg := rawCfg.DNS cfg := rawCfg.DNS
if cfg.Enable && len(cfg.NameServer) == 0 { if cfg.Enable && len(cfg.NameServer) == 0 {
return nil, fmt.Errorf("if DNS configuration is turned on, NameServer cannot be empty") return nil, fmt.Errorf("if DNS configuration is turned on, NameServer cannot be empty")
@ -1120,7 +1104,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul
return nil, err return nil, err
} }
if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy, ruleProviders, cfg.PreferH3); err != nil { if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy, cfg.PreferH3); err != nil {
return nil, err return nil, err
} }
@ -1340,8 +1324,24 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) {
} }
sniffer.Sniffers = loadSniffer sniffer.Sniffers = loadSniffer
sniffer.ForceDomain = trie.NewDomainSet(snifferRaw.ForceDomain) sniffer.ForceDomain = trie.New[struct{}]()
sniffer.SkipDomain = trie.NewDomainSet(snifferRaw.SkipDomain) for _, domain := range snifferRaw.ForceDomain {
err := sniffer.ForceDomain.Insert(domain, struct{}{})
if err != nil {
return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err)
}
}
sniffer.ForceDomain.Optimize()
sniffer.SkipDomain = trie.New[struct{}]()
for _, domain := range snifferRaw.SkipDomain {
err := sniffer.SkipDomain.Insert(domain, struct{}{})
if err != nil {
return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err)
}
}
sniffer.SkipDomain.Optimize()
return sniffer, nil return sniffer, nil
} }

View File

@ -1,5 +0,0 @@
package features
func init() {
TAGS = append(TAGS, "with_low_memory")
}

View File

@ -0,0 +1,7 @@
//go:build no_doq
package features
func init() {
TAGS = append(TAGS, "no_doq")
}

View File

@ -1,7 +0,0 @@
//go:build no_fake_tcp
package features
func init() {
TAGS = append(TAGS, "no_fake_tcp")
}

View File

@ -0,0 +1,7 @@
//go:build no_gvisor
package features
func init() {
TAGS = append(TAGS, "no_gvisor")
}

View File

@ -1,7 +0,0 @@
//go:build with_gvisor
package features
func init() {
TAGS = append(TAGS, "with_gvisor")
}

View File

@ -66,7 +66,7 @@ func (p *path) MMDB() string {
// 目录则直接跳过 // 目录则直接跳过
continue continue
} else { } else {
if strings.EqualFold(strings.ToLower(fi.Name()), "country.mmdb") { if strings.EqualFold(fi.Name(), "Country.mmdb") {
GeoipName = fi.Name() GeoipName = fi.Name()
return P.Join(p.homeDir, fi.Name()) return P.Join(p.homeDir, fi.Name())
} }
@ -93,7 +93,7 @@ func (p *path) GeoIP() string {
// 目录则直接跳过 // 目录则直接跳过
continue continue
} else { } else {
if strings.EqualFold(strings.ToLower(fi.Name()), "geoip.dat") { if strings.EqualFold(fi.Name(), "GeoIP.dat") {
GeoipName = fi.Name() GeoipName = fi.Name()
return P.Join(p.homeDir, fi.Name()) return P.Join(p.homeDir, fi.Name())
} }
@ -112,7 +112,7 @@ func (p *path) GeoSite() string {
// 目录则直接跳过 // 目录则直接跳过
continue continue
} else { } else {
if strings.EqualFold(strings.ToLower(fi.Name()), "geosite.dat") { if strings.EqualFold(fi.Name(), "GeoSite.dat") {
GeositeName = fi.Name() GeositeName = fi.Name()
return P.Join(p.homeDir, fi.Name()) return P.Join(p.homeDir, fi.Name())
} }

View File

@ -16,7 +16,6 @@ import (
"github.com/Dreamacro/clash/component/resolver" "github.com/Dreamacro/clash/component/resolver"
"github.com/Dreamacro/clash/component/trie" "github.com/Dreamacro/clash/component/trie"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/constant/provider"
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"
D "github.com/miekg/dns" D "github.com/miekg/dns"
@ -41,11 +40,6 @@ type geositePolicyRecord struct {
inversedMatching bool inversedMatching bool
} }
type domainSetPolicyRecord struct {
domainSetProvider provider.RuleProvider
policy *Policy
}
type Resolver struct { type Resolver struct {
ipv6 bool ipv6 bool
ipv6Timeout time.Duration ipv6Timeout time.Duration
@ -57,7 +51,6 @@ type Resolver struct {
group singleflight.Group group singleflight.Group
lruCache *cache.LruCache[string, *D.Msg] lruCache *cache.LruCache[string, *D.Msg]
policy *trie.DomainTrie[*Policy] policy *trie.DomainTrie[*Policy]
domainSetPolicy []domainSetPolicyRecord
geositePolicy []geositePolicyRecord geositePolicy []geositePolicyRecord
proxyServer []dnsClient proxyServer []dnsClient
} }
@ -308,12 +301,6 @@ func (r *Resolver) matchPolicy(m *D.Msg) []dnsClient {
return geositeRecord.policy.GetData() return geositeRecord.policy.GetData()
} }
} }
metadata := &C.Metadata{Host: domain}
for _, domainSetRecord := range r.domainSetPolicy {
if ok := domainSetRecord.domainSetProvider.Match(metadata); ok {
return domainSetRecord.policy.GetData()
}
}
return nil return nil
} }
@ -435,18 +422,16 @@ type FallbackFilter struct {
} }
type Config struct { type Config struct {
Main, Fallback []NameServer Main, Fallback []NameServer
Default []NameServer Default []NameServer
ProxyServer []NameServer ProxyServer []NameServer
IPv6 bool IPv6 bool
IPv6Timeout uint IPv6Timeout uint
EnhancedMode C.DNSMode EnhancedMode C.DNSMode
FallbackFilter FallbackFilter FallbackFilter FallbackFilter
Pool *fakeip.Pool Pool *fakeip.Pool
Hosts *trie.DomainTrie[resolver.HostValue] Hosts *trie.DomainTrie[resolver.HostValue]
Policy map[string][]NameServer Policy map[string][]NameServer
DomainSetPolicy map[provider.RuleProvider][]NameServer
GeositePolicy map[router.DomainMatcher][]NameServer
} }
func NewResolver(config Config) *Resolver { func NewResolver(config Config) *Resolver {
@ -498,14 +483,6 @@ func NewResolver(config Config) *Resolver {
} }
r.policy.Optimize() r.policy.Optimize()
} }
if len(config.DomainSetPolicy) > 0 {
for p, n := range config.DomainSetPolicy {
r.domainSetPolicy = append(r.domainSetPolicy, domainSetPolicyRecord{
domainSetProvider: p,
policy: NewPolicy(transform(n, defaultResolver)),
})
}
}
fallbackIPFilters := []fallbackIPFilter{} fallbackIPFilters := []fallbackIPFilter{}
if config.FallbackFilter.GeoIP { if config.FallbackFilter.GeoIP {

View File

@ -238,7 +238,6 @@ dns:
- https://doh.pub/dns-query - https://doh.pub/dns-query
- https://dns.alidns.com/dns-query - https://dns.alidns.com/dns-query
"www.baidu.com,+.google.cn": [223.5.5.5, https://dns.alidns.com/dns-query] "www.baidu.com,+.google.cn": [223.5.5.5, https://dns.alidns.com/dns-query]
# "rule-set:global,dns": 8.8.8.8 # globaldns 为 rule-providers 中的名为 global 和 dns 的规则提供器名字,且 behavior 必须为 domain
proxies: # socks5 proxies: # socks5
- name: "socks" - name: "socks"

6
go.mod
View File

@ -51,11 +51,6 @@ require (
lukechampine.com/blake3 v1.1.7 lukechampine.com/blake3 v1.1.7
) )
require (
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
)
require ( require (
github.com/ajg/form v1.5.1 // indirect github.com/ajg/form v1.5.1 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect github.com/andybalholm/brotli v1.0.5 // indirect
@ -72,7 +67,6 @@ require (
github.com/mdlayher/socket v0.4.0 // indirect github.com/mdlayher/socket v0.4.0 // indirect
github.com/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03 // indirect github.com/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03 // indirect
github.com/onsi/ginkgo/v2 v2.2.0 // indirect github.com/onsi/ginkgo/v2 v2.2.0 // indirect
github.com/openacid/low v0.1.21
github.com/oschwald/maxminddb-golang v1.10.0 // indirect github.com/oschwald/maxminddb-golang v1.10.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect

16
go.sum
View File

@ -6,7 +6,6 @@ github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@ -35,7 +34,6 @@ github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
@ -77,8 +75,6 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02
github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE= github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc=
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=
@ -109,21 +105,13 @@ github.com/miekg/dns v1.1.52 h1:Bmlc/qsNNULOe6bpXcUTsuOajd0DzRHwup6D9k1An0c=
github.com/miekg/dns v1.1.52/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/miekg/dns v1.1.52/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/mroth/weightedrand/v2 v2.0.0 h1:ADehnByWbliEDIazDAKFdBHoqgHSXAkgyKqM/9YsPoo= github.com/mroth/weightedrand/v2 v2.0.0 h1:ADehnByWbliEDIazDAKFdBHoqgHSXAkgyKqM/9YsPoo=
github.com/mroth/weightedrand/v2 v2.0.0/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU= github.com/mroth/weightedrand/v2 v2.0.0/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI= github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI=
github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q=
github.com/openacid/errors v0.8.1/go.mod h1:GUQEJJOJE3W9skHm8E8Y4phdl2LLEN8iD7c5gcGgdx0=
github.com/openacid/low v0.1.21 h1:Tr2GNu4N/+rGRYdOsEHOE89cxUIaDViZbVmKz29uKGo=
github.com/openacid/low v0.1.21/go.mod h1:q+MsKI6Pz2xsCkzV4BLj7NR5M4EX0sGz5AqotpZDVh0=
github.com/openacid/must v0.1.3/go.mod h1:luPiXCuJlEo3UUFQngVQokV0MPGryeYvtCbQPs3U1+I=
github.com/openacid/testkeys v0.1.6/go.mod h1:MfA7cACzBpbiwekivj8StqX0WIRmqlMsci1c37CA3Do=
github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs= github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs=
github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw= github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw=
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg= github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0= github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
@ -160,7 +148,6 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@ -256,8 +243,7 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d h1:qp0AnQCvRCMlu9jBjtdbTaaEmThIgZOrbVyDEOcmKhQ= google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d h1:qp0AnQCvRCMlu9jBjtdbTaaEmThIgZOrbVyDEOcmKhQ=
google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

View File

@ -5,7 +5,6 @@ import (
"net/netip" "net/netip"
"os" "os"
"runtime" "runtime"
"strings"
"sync" "sync"
"github.com/Dreamacro/clash/adapter" "github.com/Dreamacro/clash/adapter"
@ -92,7 +91,7 @@ func ApplyConfig(cfg *config.Config, force bool) {
updateSniffer(cfg.Sniffer) updateSniffer(cfg.Sniffer)
updateHosts(cfg.Hosts) updateHosts(cfg.Hosts)
updateGeneral(cfg.General) updateGeneral(cfg.General)
updateDNS(cfg.DNS, cfg.RuleProviders, cfg.General.IPv6) updateDNS(cfg.DNS, cfg.General.IPv6)
updateListeners(cfg.General, cfg.Listeners, force) updateListeners(cfg.General, cfg.Listeners, force)
updateIPTables(cfg) updateIPTables(cfg)
updateTun(cfg.General) updateTun(cfg.General)
@ -105,7 +104,7 @@ func ApplyConfig(cfg *config.Config, force bool) {
loadProxyProvider(cfg.Providers) loadProxyProvider(cfg.Providers)
updateProfile(cfg) updateProfile(cfg)
loadRuleProvider(cfg.RuleProviders) loadRuleProvider(cfg.RuleProviders)
runtime.GC()
tunnel.OnRunning() tunnel.OnRunning()
log.SetLevel(cfg.General.LogLevel) log.SetLevel(cfg.General.LogLevel)
@ -176,9 +175,10 @@ func updateListeners(general *config.General, listeners map[string]C.InboundList
} }
func updateExperimental(c *config.Config) { func updateExperimental(c *config.Config) {
runtime.GC()
} }
func updateDNS(c *config.DNS, ruleProvider map[string]provider.RuleProvider, generalIPv6 bool) { func updateDNS(c *config.DNS, generalIPv6 bool) {
if !c.Enable { if !c.Enable {
resolver.DefaultResolver = nil resolver.DefaultResolver = nil
resolver.DefaultHostMapper = nil resolver.DefaultHostMapper = nil
@ -186,25 +186,7 @@ func updateDNS(c *config.DNS, ruleProvider map[string]provider.RuleProvider, gen
dns.ReCreateServer("", nil, nil) dns.ReCreateServer("", nil, nil)
return return
} }
policy := make(map[string][]dns.NameServer)
domainSetPolicies := make(map[provider.RuleProvider][]dns.NameServer)
for key, nameservers := range c.NameServerPolicy {
temp := strings.Split(key, ":")
if len(temp) == 2 {
prefix := temp[0]
key := temp[1]
switch strings.ToLower(prefix) {
case "rule-set":
if p, ok := ruleProvider[key]; ok {
domainSetPolicies[p] = nameservers
}
case "geosite":
// TODO:
}
} else {
policy[key] = nameservers
}
}
cfg := dns.Config{ cfg := dns.Config{
Main: c.NameServer, Main: c.NameServer,
Fallback: c.Fallback, Fallback: c.Fallback,
@ -220,10 +202,9 @@ func updateDNS(c *config.DNS, ruleProvider map[string]provider.RuleProvider, gen
Domain: c.FallbackFilter.Domain, Domain: c.FallbackFilter.Domain,
GeoSite: c.FallbackFilter.GeoSite, GeoSite: c.FallbackFilter.GeoSite,
}, },
Default: c.DefaultNameserver, Default: c.DefaultNameserver,
Policy: c.NameServerPolicy, Policy: c.NameServerPolicy,
ProxyServer: c.ProxyServerNameserver, ProxyServer: c.ProxyServerNameserver,
DomainSetPolicy: domainSetPolicies,
} }
r := dns.NewResolver(cfg) r := dns.NewResolver(cfg)

View File

@ -47,7 +47,7 @@ func restart(w http.ResponseWriter, r *http.Request) {
cmd.Stderr = os.Stderr cmd.Stderr = os.Stderr
err = cmd.Start() err = cmd.Start()
if err != nil { if err != nil {
log.Fatalln("restarting:: %s", err) log.Fatalln("restarting: %s", err)
} }
os.Exit(0) os.Exit(0)

View File

@ -1,18 +1,12 @@
package route package route
import ( import (
"fmt"
"net/http" "net/http"
"os"
"os/exec"
"runtime"
"syscall"
"github.com/Dreamacro/clash/hub/updater" "github.com/Dreamacro/clash/hub/updater"
"github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/log"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-chi/render"
) )
func upgradeRouter() http.Handler { func upgradeRouter() http.Handler {
@ -30,41 +24,5 @@ func upgrade(w http.ResponseWriter, r *http.Request) {
return return
} }
execPath, err := os.Executable() restart(w, r)
if err != nil {
render.Status(r, http.StatusInternalServerError)
render.JSON(w, r, newError(fmt.Sprintf("getting path: %s", err)))
return
}
render.JSON(w, r, render.M{"status": "ok"})
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
// modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L180
// The background context is used because the underlying functions wrap it
// with timeout and shut down the server, which handles current request. It
// also should be done in a separate goroutine for the same reason.
go func() {
if runtime.GOOS == "windows" {
cmd := exec.Command(execPath, os.Args[1:]...)
log.Infoln("restarting: %q %q", execPath, os.Args[1:])
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Start()
if err != nil {
log.Fatalln("restarting: %s", err)
}
os.Exit(0)
}
log.Infoln("restarting: %q %q", execPath, os.Args[1:])
err = syscall.Exec(execPath, os.Args, os.Environ())
if err != nil {
log.Fatalln("restarting: %s", err)
}
}()
} }

View File

@ -22,8 +22,6 @@ import (
// modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/updater/updater.go // modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/updater/updater.go
// Updater is the Clash.Meta updater. // Updater is the Clash.Meta updater.
var ( var (
client http.Client
goarch string goarch string
goos string goos string
goarm string goarm string
@ -34,18 +32,15 @@ var (
// mu protects all fields below. // mu protects all fields below.
mu sync.RWMutex mu sync.RWMutex
// TODO(a.garipov): See if all of these fields actually have to be in
// this struct.
currentExeName string // 当前可执行文件 currentExeName string // 当前可执行文件
updateDir string // 更新目录 updateDir string // 更新目录
packageName string // 更新压缩文件 packageName string // 更新压缩文件
backupDir string // 备份目录 backupDir string // 备份目录
backupExeName string // 备份文件名 backupExeName string // 备份文件名
updateExeName string // 更新后的可执行文件 updateExeName string // 更新后的可执行文件
unpackedFile string
baseURL string = "https://testingcf.jsdelivr.net/gh/MetaCubeX/Clash.Meta@release/clash.meta" baseURL string = "https://github.com/MetaCubeX/Clash.Meta/releases/download/Prerelease-Alpha/clash.meta"
versionURL string = "https://raw.githubusercontent.com/MetaCubeX/Clash.Meta/release/version.txt" versionURL string = "https://github.com/MetaCubeX/Clash.Meta/releases/download/Prerelease-Alpha/version.txt"
packageURL string packageURL string
latestVersion string latestVersion string
) )
@ -61,6 +56,9 @@ func (e *updateError) Error() string {
// Update performs the auto-updater. It returns an error if the updater failed. // Update performs the auto-updater. It returns an error if the updater failed.
// If firstRun is true, it assumes the configuration file doesn't exist. // If firstRun is true, it assumes the configuration file doesn't exist.
func Update() (err error) { func Update() (err error) {
mu.Lock()
defer mu.Unlock()
goos = runtime.GOOS goos = runtime.GOOS
goarch = runtime.GOARCH goarch = runtime.GOARCH
latestVersion, err = getLatestVersion() latestVersion, err = getLatestVersion()
@ -69,16 +67,14 @@ func Update() (err error) {
return err return err
} }
log.Infoln("current version alpha-%s, latest version alpha-%s", constant.Version, latestVersion)
if latestVersion == constant.Version { if latestVersion == constant.Version {
err := &updateError{Message: "Already using latest version"} err := &updateError{Message: "Already using latest version"}
return err return err
} }
updateDownloadURL() updateDownloadURL()
mu.Lock()
defer mu.Unlock()
log.Infoln("current version alpha-%s", constant.Version)
defer func() { defer func() {
if err != nil { if err != nil {
@ -94,7 +90,6 @@ func Update() (err error) {
} }
workDir = filepath.Dir(execPath) workDir = filepath.Dir(execPath)
//log.Infoln("workDir %s", execPath)
err = prepare(execPath) err = prepare(execPath)
if err != nil { if err != nil {
@ -113,6 +108,11 @@ func Update() (err error) {
return fmt.Errorf("unpacking: %w", err) return fmt.Errorf("unpacking: %w", err)
} }
err = backup()
if err != nil {
return fmt.Errorf("replacing: %w", err)
}
err = replace() err = replace()
if err != nil { if err != nil {
return fmt.Errorf("replacing: %w", err) return fmt.Errorf("replacing: %w", err)
@ -140,7 +140,7 @@ func prepare(exePath string) (err error) {
updateExeName = "clash.meta" + "-" + goos + "-" + goarch updateExeName = "clash.meta" + "-" + goos + "-" + goarch
} }
log.Infoln("updateExeName: %s ,currentExeName: %s", updateExeName, currentExeName) log.Infoln("updateExeName: %s ", updateExeName)
backupExeName = filepath.Join(backupDir, filepath.Base(exePath)) backupExeName = filepath.Join(backupDir, filepath.Base(exePath))
updateExeName = filepath.Join(updateDir, updateExeName) updateExeName = filepath.Join(updateDir, updateExeName)
@ -166,13 +166,13 @@ func unpack() error {
log.Debugln("updater: unpacking package") log.Debugln("updater: unpacking package")
if strings.HasSuffix(pkgNameOnly, ".zip") { if strings.HasSuffix(pkgNameOnly, ".zip") {
unpackedFile, err = zipFileUnpack(packageName, updateDir) _, err = zipFileUnpack(packageName, updateDir)
if err != nil { if err != nil {
return fmt.Errorf(".zip unpack failed: %w", err) return fmt.Errorf(".zip unpack failed: %w", err)
} }
} else if strings.HasSuffix(pkgNameOnly, ".gz") { } else if strings.HasSuffix(pkgNameOnly, ".gz") {
unpackedFile, err = gzFileUnpack(packageName, updateDir) _, err = gzFileUnpack(packageName, updateDir)
if err != nil { if err != nil {
return fmt.Errorf(".gz unpack failed: %w", err) return fmt.Errorf(".gz unpack failed: %w", err)
} }
@ -184,25 +184,37 @@ func unpack() error {
return nil return nil
} }
// backup makes a backup of the current configuration and supporting files. It
// ignores the configuration file if firstRun is true.
func backup() (err error) {
log.Infoln("updater: backing up current Exefile")
_ = os.Mkdir(backupDir, 0o755)
err = copyFile(currentExeName, backupExeName)
if err != nil {
return fmt.Errorf("copySupportingFiles(%s, %s) failed: %w", currentExeName, backupExeName, err)
}
return nil
}
// replace moves the current executable with the updated one and also copies the // replace moves the current executable with the updated one and also copies the
// supporting files. // supporting files.
func replace() error { func replace() error {
//err := copySupportingFiles(unpackedFiles, updateDir, workDir) var err error
//if err != nil {
// return fmt.Errorf("copySupportingFiles(%s, %s) failed: %w", updateDir, workDir, err)
//}
log.Infoln("updater: renaming: %s to %s", currentExeName, backupExeName) // log.Infoln("updater: renaming: %s to %s", currentExeName, backupExeName)
err := os.Rename(currentExeName, backupExeName) // err := os.Rename(currentExeName, backupExeName)
if err != nil { // if err != nil {
return err // return err
} // }
if goos == "windows" { if goos == "windows" {
// rename fails with "File in use" error // rename fails with "File in use" error
log.Infoln("copying:%s to %s", updateExeName, currentExeName) log.Infoln("copying: %s to %s", updateExeName, currentExeName)
err = copyFile(updateExeName, currentExeName) err = copyFile(updateExeName, currentExeName)
} else { } else {
log.Infoln("copying: %s to %s", updateExeName, currentExeName)
err = os.Rename(updateExeName, currentExeName) err = os.Rename(updateExeName, currentExeName)
} }
if err != nil { if err != nil {
@ -256,11 +268,11 @@ func downloadPackageFile() (err error) {
log.Debugln("updateDir %s", updateDir) log.Debugln("updateDir %s", updateDir)
err = os.Mkdir(updateDir, 0o755) err = os.Mkdir(updateDir, 0o755)
if err != nil { if err != nil {
fmt.Errorf("mkdir error: %w", err) return fmt.Errorf("mkdir error: %w", err)
} }
log.Debugln("updater: saving package to file %s", packageName) log.Debugln("updater: saving package to file %s", packageName)
err = os.WriteFile(packageName, body, 0o755) err = os.WriteFile(packageName, body, 0o644)
if err != nil { if err != nil {
return fmt.Errorf("os.WriteFile() failed: %w", err) return fmt.Errorf("os.WriteFile() failed: %w", err)
} }
@ -425,7 +437,6 @@ func getLatestVersion() (version string, err error) {
return "", fmt.Errorf("get Latest Version fail: %w", err) return "", fmt.Errorf("get Latest Version fail: %w", err)
} }
content := strings.TrimRight(string(body), "\n") content := strings.TrimRight(string(body), "\n")
log.Infoln("latest:%s", content)
return content, nil return content, nil
} }

View File

@ -110,10 +110,7 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
conn2 = nil conn2 = nil
}() }()
for { for {
// safe size which is 1232 from https://dnsflagday.net/2020/. buff := buf.NewPacket()
// so 2048 is enough
buff := buf.NewSize(2 * 1024)
_ = conn.SetReadDeadline(time.Now().Add(DefaultDnsReadTimeout))
dest, err := conn.ReadPacket(buff) dest, err := conn.ReadPacket(buff)
if err != nil { if err != nil {
buff.Release() buff.Release()

View File

@ -1,6 +1,7 @@
package common package common
import ( import (
"golang.org/x/net/idna"
"strings" "strings"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
@ -10,6 +11,7 @@ type Domain struct {
*Base *Base
domain string domain string
adapter string adapter string
isIDNA bool
} }
func (d *Domain) RuleType() C.RuleType { func (d *Domain) RuleType() C.RuleType {
@ -25,14 +27,20 @@ func (d *Domain) Adapter() string {
} }
func (d *Domain) Payload() string { func (d *Domain) Payload() string {
return d.domain domain := d.domain
if d.isIDNA {
domain, _ = idna.ToUnicode(domain)
}
return domain
} }
func NewDomain(domain string, adapter string) *Domain { func NewDomain(domain string, adapter string) *Domain {
actualDomain, _ := idna.ToASCII(domain)
return &Domain{ return &Domain{
Base: &Base{}, Base: &Base{},
domain: strings.ToLower(domain), domain: strings.ToLower(actualDomain),
adapter: adapter, adapter: adapter,
isIDNA: actualDomain != domain,
} }
} }

View File

@ -1,6 +1,7 @@
package common package common
import ( import (
"golang.org/x/net/idna"
"strings" "strings"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
@ -10,6 +11,7 @@ type DomainKeyword struct {
*Base *Base
keyword string keyword string
adapter string adapter string
isIDNA bool
} }
func (dk *DomainKeyword) RuleType() C.RuleType { func (dk *DomainKeyword) RuleType() C.RuleType {
@ -26,14 +28,20 @@ func (dk *DomainKeyword) Adapter() string {
} }
func (dk *DomainKeyword) Payload() string { func (dk *DomainKeyword) Payload() string {
return dk.keyword keyword := dk.keyword
if dk.isIDNA {
keyword, _ = idna.ToUnicode(keyword)
}
return keyword
} }
func NewDomainKeyword(keyword string, adapter string) *DomainKeyword { func NewDomainKeyword(keyword string, adapter string) *DomainKeyword {
actualDomainKeyword, _ := idna.ToASCII(keyword)
return &DomainKeyword{ return &DomainKeyword{
Base: &Base{}, Base: &Base{},
keyword: strings.ToLower(keyword), keyword: strings.ToLower(actualDomainKeyword),
adapter: adapter, adapter: adapter,
isIDNA: keyword != actualDomainKeyword,
} }
} }

View File

@ -1,6 +1,7 @@
package common package common
import ( import (
"golang.org/x/net/idna"
"strings" "strings"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
@ -10,6 +11,7 @@ type DomainSuffix struct {
*Base *Base
suffix string suffix string
adapter string adapter string
isIDNA bool
} }
func (ds *DomainSuffix) RuleType() C.RuleType { func (ds *DomainSuffix) RuleType() C.RuleType {
@ -26,14 +28,20 @@ func (ds *DomainSuffix) Adapter() string {
} }
func (ds *DomainSuffix) Payload() string { func (ds *DomainSuffix) Payload() string {
return ds.suffix suffix := ds.suffix
if ds.isIDNA {
suffix, _ = idna.ToUnicode(suffix)
}
return suffix
} }
func NewDomainSuffix(suffix string, adapter string) *DomainSuffix { func NewDomainSuffix(suffix string, adapter string) *DomainSuffix {
actualDomainSuffix, _ := idna.ToASCII(suffix)
return &DomainSuffix{ return &DomainSuffix{
Base: &Base{}, Base: &Base{},
suffix: strings.ToLower(suffix), suffix: strings.ToLower(actualDomainSuffix),
adapter: adapter, adapter: adapter,
isIDNA: suffix != actualDomainSuffix,
} }
} }

View File

@ -21,8 +21,10 @@ func NewNetworkType(network, adapter string) (*NetworkType, error) {
switch strings.ToUpper(network) { switch strings.ToUpper(network) {
case "TCP": case "TCP":
ntType.network = C.TCP ntType.network = C.TCP
break
case "UDP": case "UDP":
ntType.network = C.UDP ntType.network = C.UDP
break
default: default:
return nil, fmt.Errorf("unsupported network type, only TCP/UDP") return nil, fmt.Errorf("unsupported network type, only TCP/UDP")
} }

View File

@ -3,11 +3,13 @@ package provider
import ( import (
"github.com/Dreamacro/clash/component/trie" "github.com/Dreamacro/clash/component/trie"
C "github.com/Dreamacro/clash/constant" C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
"golang.org/x/net/idna"
) )
type domainStrategy struct { type domainStrategy struct {
count int count int
domainRules *trie.DomainSet domainRules *trie.DomainTrie[struct{}]
} }
func (d *domainStrategy) ShouldFindProcess() bool { func (d *domainStrategy) ShouldFindProcess() bool {
@ -15,7 +17,7 @@ func (d *domainStrategy) ShouldFindProcess() bool {
} }
func (d *domainStrategy) Match(metadata *C.Metadata) bool { func (d *domainStrategy) Match(metadata *C.Metadata) bool {
return d.domainRules != nil && d.domainRules.Has(metadata.RuleHost()) return d.domainRules != nil && d.domainRules.Search(metadata.RuleHost()) != nil
} }
func (d *domainStrategy) Count() int { func (d *domainStrategy) Count() int {
@ -27,9 +29,21 @@ func (d *domainStrategy) ShouldResolveIP() bool {
} }
func (d *domainStrategy) OnUpdate(rules []string) { func (d *domainStrategy) OnUpdate(rules []string) {
domainTrie := trie.NewDomainSet(rules) domainTrie := trie.New[struct{}]()
count := 0
for _, rule := range rules {
actualDomain, _ := idna.ToASCII(rule)
err := domainTrie.Insert(actualDomain, struct{}{})
if err != nil {
log.Warnln("invalid domain:[%s]", rule)
} else {
count++
}
}
domainTrie.Optimize()
d.domainRules = domainTrie d.domainRules = domainTrie
d.count = len(rules) d.count = count
} }
func NewDomainStrategy() *domainStrategy { func NewDomainStrategy() *domainStrategy {

View File

@ -1,5 +1,5 @@
//go:build linux && !no_fake_tcp //go:build linux
// +build linux,!no_fake_tcp // +build linux
package faketcp package faketcp

View File

@ -1,5 +1,5 @@
//go:build !linux || no_fake_tcp //go:build !linux
// +build !linux no_fake_tcp // +build !linux
package faketcp package faketcp