feat: nameserver-policy
support use rule-providers and reduce domain-set memory
This commit is contained in:
parent
991de009be
commit
cfd03a99c2
30 changed files with 503 additions and 141 deletions
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)
|
||||||
|
}
|
|
@ -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")
|
||||||
|
}
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue