Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4e48d709f | ||
|
|
f54bf2d935 | ||
|
|
971131d951 | ||
|
|
07fb529e36 | ||
|
|
a6ee3348df | ||
|
|
53b1250cd8 | ||
|
|
3a4b5fb145 | ||
|
|
858d9de250 | ||
|
|
1e989b68bd | ||
|
|
752074c68e | ||
|
|
d0663277dc | ||
|
|
14a0a3c790 | ||
|
|
6c9c0bd755 | ||
|
|
e0cf342672 | ||
|
|
7b7a8981a6 | ||
|
|
d462257e53 | ||
|
|
2e1c5091c3 | ||
|
|
ae13418929 | ||
|
|
ae8913587d | ||
|
|
c486d7c67e | ||
|
|
4c4f734572 | ||
|
|
82dddf932a | ||
|
|
75ed879121 | ||
|
|
5b73942960 | ||
|
|
d305e0ddfc | ||
|
|
9f7a0052a8 | ||
|
|
c6873c5c04 | ||
|
|
c3ed06e99d | ||
|
|
d52748165f | ||
|
|
56e525114d |
16
.github/workflows/Delete.yml
vendored
16
.github/workflows/Delete.yml
vendored
@@ -1,16 +0,0 @@
|
|||||||
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
|
|
||||||
12
.github/workflows/build.yml
vendored
12
.github/workflows/build.yml
vendored
@@ -268,6 +268,18 @@ 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
16
.github/workflows/delete.yml
vendored
@@ -1,16 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -30,7 +30,8 @@
|
|||||||
- 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
|
||||||
|
|
||||||
|
|||||||
@@ -42,8 +42,6 @@ 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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
common/pool/buffer_low_memory.go
Normal file
15
common/pool/buffer_low_memory.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
//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
|
||||||
|
)
|
||||||
15
common/pool/buffer_standard.go
Normal file
15
common/pool/buffer_standard.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
//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
|
||||||
|
)
|
||||||
@@ -1,17 +1,5 @@
|
|||||||
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)
|
||||||
}
|
}
|
||||||
|
|||||||
9
common/utils/strings.go
Normal file
9
common/utils/strings.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
@@ -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, "")
|
conn := inner.HandleTcp(address, urlRes.Hostname())
|
||||||
return conn, nil
|
return conn, nil
|
||||||
},
|
},
|
||||||
TLSClientConfig: tls.GetDefaultTLSConfig(),
|
TLSClientConfig: tls.GetDefaultTLSConfig(),
|
||||||
|
|||||||
@@ -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.DomainTrie[struct{}]
|
forceDomain *trie.DomainSet
|
||||||
skipSNI *trie.DomainTrie[struct{}]
|
skipSNI *trie.DomainSet
|
||||||
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.Search(metadata.Host) != nil || (metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping) {
|
if (metadata.Host == "" && sd.parsePureIp) || sd.forceDomain.Has(metadata.Host) || (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.Search(host) != nil {
|
if sd.skipSNI.Has(host) {
|
||||||
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, forceDomain *trie.DomainTrie[struct{}],
|
func NewSnifferDispatcher(snifferConfig map[sniffer.Type]SnifferConfig,
|
||||||
skipSNI *trie.DomainTrie[struct{}],
|
forceDomain *trie.DomainSet, skipSNI *trie.DomainSet,
|
||||||
forceDnsMapping bool, parsePureIp bool) (*SnifferDispatcher, error) {
|
forceDnsMapping bool, parsePureIp bool) (*SnifferDispatcher, error) {
|
||||||
dispatcher := SnifferDispatcher{
|
dispatcher := SnifferDispatcher{
|
||||||
enable: true,
|
enable: true,
|
||||||
|
|||||||
@@ -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,12 +40,22 @@ func ResetCertificate() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getCertPool() *x509.CertPool {
|
func getCertPool() *x509.CertPool {
|
||||||
|
if len(trustCerts) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if certPool == nil {
|
||||||
|
mutex.Lock()
|
||||||
|
defer mutex.Unlock()
|
||||||
|
if certPool != nil {
|
||||||
|
return certPool
|
||||||
|
}
|
||||||
certPool, err := x509.SystemCertPool()
|
certPool, err := x509.SystemCertPool()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
for _, cert := range trustCerts {
|
for _, cert := range trustCerts {
|
||||||
certPool.AddCert(cert)
|
certPool.AddCert(cert)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return certPool
|
return certPool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,6 +123,30 @@ 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]()}
|
||||||
|
|||||||
@@ -105,3 +105,23 @@ 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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -116,6 +116,18 @@ 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
|
||||||
}
|
}
|
||||||
|
|||||||
60
component/trie/set_test.go
Normal file
60
component/trie/set_test.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
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"))
|
||||||
|
}
|
||||||
178
component/trie/sskv.go
Normal file
178
component/trie/sskv.go
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
@@ -9,7 +9,6 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"runtime"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -136,9 +135,8 @@ 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
|
||||||
Reverses *trie.DomainTrie[struct{}]
|
ForceDomain *trie.DomainSet
|
||||||
ForceDomain *trie.DomainTrie[struct{}]
|
SkipDomain *trie.DomainSet
|
||||||
SkipDomain *trie.DomainTrie[struct{}]
|
|
||||||
ForceDnsMapping bool
|
ForceDnsMapping bool
|
||||||
ParsePureIp bool
|
ParsePureIp bool
|
||||||
}
|
}
|
||||||
@@ -490,7 +488,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
|
|||||||
}
|
}
|
||||||
config.Hosts = hosts
|
config.Hosts = hosts
|
||||||
|
|
||||||
dnsCfg, err := parseDNS(rawCfg, hosts, rules)
|
dnsCfg, err := parseDNS(rawCfg, hosts, rules, ruleProviders)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -822,8 +820,6 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -983,7 +979,7 @@ func parsePureDNSServer(server string) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func parseNameServerPolicy(nsPolicy map[string]any, preferH3 bool) (map[string][]dns.NameServer, error) {
|
func parseNameServerPolicy(nsPolicy map[string]any, ruleProviders map[string]providerTypes.RuleProvider, 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,})?`)
|
||||||
@@ -998,6 +994,14 @@ func parseNameServerPolicy(nsPolicy map[string]any, preferH3 bool) (map[string][
|
|||||||
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 {
|
||||||
@@ -1021,6 +1025,19 @@ func parseNameServerPolicy(nsPolicy map[string]any, preferH3 bool) (map[string][
|
|||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1073,11 +1090,10 @@ 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) (*DNS, error) {
|
func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rules []C.Rule, ruleProviders map[string]providerTypes.RuleProvider) (*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")
|
||||||
@@ -1104,7 +1120,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy, cfg.PreferH3); err != nil {
|
if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy, ruleProviders, cfg.PreferH3); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1324,24 +1340,8 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sniffer.Sniffers = loadSniffer
|
sniffer.Sniffers = loadSniffer
|
||||||
sniffer.ForceDomain = trie.New[struct{}]()
|
sniffer.ForceDomain = trie.NewDomainSet(snifferRaw.ForceDomain)
|
||||||
for _, domain := range snifferRaw.ForceDomain {
|
sniffer.SkipDomain = trie.NewDomainSet(snifferRaw.SkipDomain)
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
5
constant/features/low_memory.go
Normal file
5
constant/features/low_memory.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package features
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
TAGS = append(TAGS, "with_low_memory")
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
//go:build no_doq
|
|
||||||
|
|
||||||
package features
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
TAGS = append(TAGS, "no_doq")
|
|
||||||
}
|
|
||||||
7
constant/features/no_fake_tcp.go
Normal file
7
constant/features/no_fake_tcp.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
//go:build no_fake_tcp
|
||||||
|
|
||||||
|
package features
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
TAGS = append(TAGS, "no_fake_tcp")
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
//go:build no_gvisor
|
|
||||||
|
|
||||||
package features
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
TAGS = append(TAGS, "no_gvisor")
|
|
||||||
}
|
|
||||||
7
constant/features/with_gvisor.go
Normal file
7
constant/features/with_gvisor.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
//go:build with_gvisor
|
||||||
|
|
||||||
|
package features
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
TAGS = append(TAGS, "with_gvisor")
|
||||||
|
}
|
||||||
@@ -66,7 +66,7 @@ func (p *path) MMDB() string {
|
|||||||
// 目录则直接跳过
|
// 目录则直接跳过
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
if strings.EqualFold(fi.Name(), "Country.mmdb") {
|
if strings.EqualFold(strings.ToLower(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(fi.Name(), "GeoIP.dat") {
|
if strings.EqualFold(strings.ToLower(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(fi.Name(), "GeoSite.dat") {
|
if strings.EqualFold(strings.ToLower(fi.Name()), "geosite.dat") {
|
||||||
GeositeName = fi.Name()
|
GeositeName = fi.Name()
|
||||||
return P.Join(p.homeDir, fi.Name())
|
return P.Join(p.homeDir, fi.Name())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ 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"
|
||||||
@@ -40,6 +41,11 @@ 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
|
||||||
@@ -51,6 +57,7 @@ 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
|
||||||
}
|
}
|
||||||
@@ -301,6 +308,12 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -432,6 +445,8 @@ type Config struct {
|
|||||||
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 {
|
||||||
@@ -483,6 +498,14 @@ 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 {
|
||||||
|
|||||||
@@ -238,6 +238,7 @@ 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 # global,dns 为 rule-providers 中的名为 global 和 dns 的规则提供器名字,且 behavior 必须为 domain
|
||||||
|
|
||||||
proxies: # socks5
|
proxies: # socks5
|
||||||
- name: "socks"
|
- name: "socks"
|
||||||
|
|||||||
6
go.mod
6
go.mod
@@ -51,6 +51,11 @@ 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
|
||||||
@@ -67,6 +72,7 @@ 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
16
go.sum
@@ -6,6 +6,7 @@ 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=
|
||||||
@@ -34,6 +35,7 @@ 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=
|
||||||
@@ -75,6 +77,8 @@ 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=
|
||||||
@@ -105,13 +109,21 @@ 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=
|
||||||
@@ -148,6 +160,7 @@ 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=
|
||||||
@@ -243,7 +256,8 @@ 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-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||||
|
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=
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/Dreamacro/clash/adapter"
|
"github.com/Dreamacro/clash/adapter"
|
||||||
@@ -91,7 +92,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.General.IPv6)
|
updateDNS(cfg.DNS, cfg.RuleProviders, 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)
|
||||||
@@ -104,7 +105,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)
|
||||||
@@ -175,10 +176,9 @@ 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, generalIPv6 bool) {
|
func updateDNS(c *config.DNS, ruleProvider map[string]provider.RuleProvider, generalIPv6 bool) {
|
||||||
if !c.Enable {
|
if !c.Enable {
|
||||||
resolver.DefaultResolver = nil
|
resolver.DefaultResolver = nil
|
||||||
resolver.DefaultHostMapper = nil
|
resolver.DefaultHostMapper = nil
|
||||||
@@ -186,7 +186,25 @@ func updateDNS(c *config.DNS, generalIPv6 bool) {
|
|||||||
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,
|
||||||
@@ -205,6 +223,7 @@ func updateDNS(c *config.DNS, generalIPv6 bool) {
|
|||||||
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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
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 {
|
||||||
@@ -24,5 +30,41 @@ func upgrade(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
restart(w, r)
|
execPath, err := os.Executable()
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ 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
|
||||||
@@ -32,15 +34,18 @@ 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://github.com/MetaCubeX/Clash.Meta/releases/download/Prerelease-Alpha/clash.meta"
|
baseURL string = "https://testingcf.jsdelivr.net/gh/MetaCubeX/Clash.Meta@release/clash.meta"
|
||||||
versionURL string = "https://github.com/MetaCubeX/Clash.Meta/releases/download/Prerelease-Alpha/version.txt"
|
versionURL string = "https://raw.githubusercontent.com/MetaCubeX/Clash.Meta/release/version.txt"
|
||||||
packageURL string
|
packageURL string
|
||||||
latestVersion string
|
latestVersion string
|
||||||
)
|
)
|
||||||
@@ -56,9 +61,6 @@ 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()
|
||||||
@@ -67,14 +69,16 @@ 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 {
|
||||||
@@ -90,6 +94,7 @@ 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 {
|
||||||
@@ -108,11 +113,6 @@ 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 ", updateExeName)
|
log.Infoln("updateExeName: %s ,currentExeName: %s", updateExeName, currentExeName)
|
||||||
|
|
||||||
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") {
|
||||||
_, err = zipFileUnpack(packageName, updateDir)
|
unpackedFile, 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") {
|
||||||
_, err = gzFileUnpack(packageName, updateDir)
|
unpackedFile, 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,37 +184,25 @@ 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 {
|
||||||
var err error
|
//err := copySupportingFiles(unpackedFiles, updateDir, workDir)
|
||||||
|
|
||||||
// log.Infoln("updater: renaming: %s to %s", currentExeName, backupExeName)
|
|
||||||
// err := os.Rename(currentExeName, backupExeName)
|
|
||||||
//if err != nil {
|
//if err != nil {
|
||||||
// return err
|
// return fmt.Errorf("copySupportingFiles(%s, %s) failed: %w", updateDir, workDir, err)
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
log.Infoln("updater: renaming: %s to %s", currentExeName, backupExeName)
|
||||||
|
err := os.Rename(currentExeName, backupExeName)
|
||||||
|
if err != nil {
|
||||||
|
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 {
|
||||||
@@ -268,11 +256,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 {
|
||||||
return fmt.Errorf("mkdir error: %w", err)
|
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, 0o644)
|
err = os.WriteFile(packageName, body, 0o755)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("os.WriteFile() failed: %w", err)
|
return fmt.Errorf("os.WriteFile() failed: %w", err)
|
||||||
}
|
}
|
||||||
@@ -437,6 +425,7 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,10 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
|
|||||||
conn2 = nil
|
conn2 = nil
|
||||||
}()
|
}()
|
||||||
for {
|
for {
|
||||||
buff := buf.NewPacket()
|
// safe size which is 1232 from https://dnsflagday.net/2020/.
|
||||||
|
// 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()
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
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"
|
||||||
@@ -11,7 +10,6 @@ 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 {
|
||||||
@@ -27,20 +25,14 @@ func (d *Domain) Adapter() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Domain) Payload() string {
|
func (d *Domain) Payload() string {
|
||||||
domain := d.domain
|
return 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(actualDomain),
|
domain: strings.ToLower(domain),
|
||||||
adapter: adapter,
|
adapter: adapter,
|
||||||
isIDNA: actualDomain != domain,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
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"
|
||||||
@@ -11,7 +10,6 @@ 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 {
|
||||||
@@ -28,20 +26,14 @@ func (dk *DomainKeyword) Adapter() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dk *DomainKeyword) Payload() string {
|
func (dk *DomainKeyword) Payload() string {
|
||||||
keyword := dk.keyword
|
return 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(actualDomainKeyword),
|
keyword: strings.ToLower(keyword),
|
||||||
adapter: adapter,
|
adapter: adapter,
|
||||||
isIDNA: keyword != actualDomainKeyword,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
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"
|
||||||
@@ -11,7 +10,6 @@ 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 {
|
||||||
@@ -28,20 +26,14 @@ func (ds *DomainSuffix) Adapter() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ds *DomainSuffix) Payload() string {
|
func (ds *DomainSuffix) Payload() string {
|
||||||
suffix := ds.suffix
|
return 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(actualDomainSuffix),
|
suffix: strings.ToLower(suffix),
|
||||||
adapter: adapter,
|
adapter: adapter,
|
||||||
isIDNA: suffix != actualDomainSuffix,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,10 +21,8 @@ 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")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,11 @@ 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.DomainTrie[struct{}]
|
domainRules *trie.DomainSet
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *domainStrategy) ShouldFindProcess() bool {
|
func (d *domainStrategy) ShouldFindProcess() bool {
|
||||||
@@ -17,7 +15,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.Search(metadata.RuleHost()) != nil
|
return d.domainRules != nil && d.domainRules.Has(metadata.RuleHost())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *domainStrategy) Count() int {
|
func (d *domainStrategy) Count() int {
|
||||||
@@ -29,21 +27,9 @@ func (d *domainStrategy) ShouldResolveIP() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *domainStrategy) OnUpdate(rules []string) {
|
func (d *domainStrategy) OnUpdate(rules []string) {
|
||||||
domainTrie := trie.New[struct{}]()
|
domainTrie := trie.NewDomainSet(rules)
|
||||||
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 = count
|
d.count = len(rules)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDomainStrategy() *domainStrategy {
|
func NewDomainStrategy() *domainStrategy {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//go:build linux
|
//go:build linux && !no_fake_tcp
|
||||||
// +build linux
|
// +build linux,!no_fake_tcp
|
||||||
|
|
||||||
package faketcp
|
package faketcp
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//go:build !linux
|
//go:build !linux || no_fake_tcp
|
||||||
// +build !linux
|
// +build !linux no_fake_tcp
|
||||||
|
|
||||||
package faketcp
|
package faketcp
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user