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
|
||||
|
||||
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 {
|
||||
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 {
|
||||
enable bool
|
||||
sniffers map[sniffer.Sniffer]SnifferConfig
|
||||
forceDomain *trie.DomainTrie[struct{}]
|
||||
skipSNI *trie.DomainTrie[struct{}]
|
||||
forceDomain *trie.DomainSet
|
||||
skipSNI *trie.DomainSet
|
||||
skipList *cache.LruCache[string, uint8]
|
||||
rwMux sync.RWMutex
|
||||
forceDnsMapping bool
|
||||
|
@ -37,7 +37,7 @@ type SnifferDispatcher struct {
|
|||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
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)
|
||||
return
|
||||
} else {
|
||||
if sd.skipSNI.Search(host) != nil {
|
||||
if sd.skipSNI.Has(host) {
|
||||
log.Debugln("[Sniffer] Skip sni[%s]", host)
|
||||
return
|
||||
}
|
||||
|
@ -166,8 +166,8 @@ func NewCloseSnifferDispatcher() (*SnifferDispatcher, error) {
|
|||
return &dispatcher, nil
|
||||
}
|
||||
|
||||
func NewSnifferDispatcher(snifferConfig map[sniffer.Type]SnifferConfig, forceDomain *trie.DomainTrie[struct{}],
|
||||
skipSNI *trie.DomainTrie[struct{}],
|
||||
func NewSnifferDispatcher(snifferConfig map[sniffer.Type]SnifferConfig,
|
||||
forceDomain *trie.DomainSet, skipSNI *trie.DomainSet,
|
||||
forceDnsMapping bool, parsePureIp bool) (*SnifferDispatcher, error) {
|
||||
dispatcher := SnifferDispatcher{
|
||||
enable: true,
|
||||
|
|
|
@ -15,7 +15,7 @@ import (
|
|||
)
|
||||
|
||||
var trustCerts []*x509.Certificate
|
||||
|
||||
var certPool *x509.CertPool
|
||||
var mutex sync.RWMutex
|
||||
var errNotMacth error = errors.New("certificate fingerprints do not match")
|
||||
|
||||
|
@ -40,10 +40,20 @@ func ResetCertificate() {
|
|||
}
|
||||
|
||||
func getCertPool() *x509.CertPool {
|
||||
certPool, err := x509.SystemCertPool()
|
||||
if err == nil {
|
||||
for _, cert := range trustCerts {
|
||||
certPool.AddCert(cert)
|
||||
if len(trustCerts) == 0 {
|
||||
return nil
|
||||
}
|
||||
if certPool == nil {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
if certPool != nil {
|
||||
return certPool
|
||||
}
|
||||
certPool, err := x509.SystemCertPool()
|
||||
if err == nil {
|
||||
for _, cert := range trustCerts {
|
||||
certPool.AddCert(cert)
|
||||
}
|
||||
}
|
||||
}
|
||||
return certPool
|
||||
|
|
|
@ -25,7 +25,7 @@ func ValidAndSplitDomain(domain string) ([]string, bool) {
|
|||
if domain != "" && domain[len(domain)-1] == '.' {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
domain=strings.ToLower(domain)
|
||||
parts := strings.Split(domain, domainStep)
|
||||
if len(parts) == 1 {
|
||||
if parts[0] == "" {
|
||||
|
@ -123,6 +123,30 @@ func (t *DomainTrie[T]) 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.
|
||||
func New[T any]() *DomainTrie[T] {
|
||||
return &DomainTrie[T]{root: newNode[T]()}
|
||||
|
|
|
@ -105,3 +105,23 @@ func TestTrie_WildcardBoundary(t *testing.T) {
|
|||
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
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"
|
||||
"os"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -136,9 +135,8 @@ type IPTables struct {
|
|||
type Sniffer struct {
|
||||
Enable bool
|
||||
Sniffers map[snifferTypes.Type]SNIFF.SnifferConfig
|
||||
Reverses *trie.DomainTrie[struct{}]
|
||||
ForceDomain *trie.DomainTrie[struct{}]
|
||||
SkipDomain *trie.DomainTrie[struct{}]
|
||||
ForceDomain *trie.DomainSet
|
||||
SkipDomain *trie.DomainSet
|
||||
ForceDnsMapping bool
|
||||
ParsePureIp bool
|
||||
}
|
||||
|
@ -490,7 +488,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) {
|
|||
}
|
||||
config.Hosts = hosts
|
||||
|
||||
dnsCfg, err := parseDNS(rawCfg, hosts, rules)
|
||||
dnsCfg, err := parseDNS(rawCfg, hosts, rules, ruleProviders)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -822,8 +820,6 @@ func parseRules(rulesConfig []string, proxies map[string]C.Proxy, subRules map[s
|
|||
rules = append(rules, parsed)
|
||||
}
|
||||
|
||||
runtime.GC()
|
||||
|
||||
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{}
|
||||
updatedPolicy := make(map[string]interface{})
|
||||
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
|
||||
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) {
|
||||
subkeys := strings.Split(k, ",")
|
||||
for _, subkey := range subkeys {
|
||||
|
@ -1021,6 +1025,19 @@ func parseNameServerPolicy(nsPolicy map[string]any, preferH3 bool) (map[string][
|
|||
if _, valid := trie.ValidAndSplitDomain(domain); !valid {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
runtime.GC()
|
||||
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
|
||||
if cfg.Enable && len(cfg.NameServer) == 0 {
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -1324,24 +1340,8 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) {
|
|||
}
|
||||
|
||||
sniffer.Sniffers = loadSniffer
|
||||
sniffer.ForceDomain = trie.New[struct{}]()
|
||||
for _, domain := range snifferRaw.ForceDomain {
|
||||
err := sniffer.ForceDomain.Insert(domain, struct{}{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err)
|
||||
}
|
||||
}
|
||||
sniffer.ForceDomain.Optimize()
|
||||
|
||||
sniffer.SkipDomain = trie.New[struct{}]()
|
||||
for _, domain := range snifferRaw.SkipDomain {
|
||||
err := sniffer.SkipDomain.Insert(domain, struct{}{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err)
|
||||
}
|
||||
}
|
||||
sniffer.SkipDomain.Optimize()
|
||||
|
||||
sniffer.ForceDomain = trie.NewDomainSet(snifferRaw.ForceDomain)
|
||||
sniffer.SkipDomain = trie.NewDomainSet(snifferRaw.SkipDomain)
|
||||
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/trie"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/constant/provider"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
|
||||
D "github.com/miekg/dns"
|
||||
|
@ -40,6 +41,11 @@ type geositePolicyRecord struct {
|
|||
inversedMatching bool
|
||||
}
|
||||
|
||||
type domainSetPolicyRecord struct {
|
||||
domainSetProvider provider.RuleProvider
|
||||
policy *Policy
|
||||
}
|
||||
|
||||
type Resolver struct {
|
||||
ipv6 bool
|
||||
ipv6Timeout time.Duration
|
||||
|
@ -51,6 +57,7 @@ type Resolver struct {
|
|||
group singleflight.Group
|
||||
lruCache *cache.LruCache[string, *D.Msg]
|
||||
policy *trie.DomainTrie[*Policy]
|
||||
domainSetPolicy []domainSetPolicyRecord
|
||||
geositePolicy []geositePolicyRecord
|
||||
proxyServer []dnsClient
|
||||
}
|
||||
|
@ -301,6 +308,12 @@ func (r *Resolver) matchPolicy(m *D.Msg) []dnsClient {
|
|||
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
|
||||
}
|
||||
|
||||
|
@ -422,16 +435,18 @@ type FallbackFilter struct {
|
|||
}
|
||||
|
||||
type Config struct {
|
||||
Main, Fallback []NameServer
|
||||
Default []NameServer
|
||||
ProxyServer []NameServer
|
||||
IPv6 bool
|
||||
IPv6Timeout uint
|
||||
EnhancedMode C.DNSMode
|
||||
FallbackFilter FallbackFilter
|
||||
Pool *fakeip.Pool
|
||||
Hosts *trie.DomainTrie[resolver.HostValue]
|
||||
Policy map[string][]NameServer
|
||||
Main, Fallback []NameServer
|
||||
Default []NameServer
|
||||
ProxyServer []NameServer
|
||||
IPv6 bool
|
||||
IPv6Timeout uint
|
||||
EnhancedMode C.DNSMode
|
||||
FallbackFilter FallbackFilter
|
||||
Pool *fakeip.Pool
|
||||
Hosts *trie.DomainTrie[resolver.HostValue]
|
||||
Policy map[string][]NameServer
|
||||
DomainSetPolicy map[provider.RuleProvider][]NameServer
|
||||
GeositePolicy map[router.DomainMatcher][]NameServer
|
||||
}
|
||||
|
||||
func NewResolver(config Config) *Resolver {
|
||||
|
@ -483,6 +498,14 @@ func NewResolver(config Config) *Resolver {
|
|||
}
|
||||
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{}
|
||||
if config.FallbackFilter.GeoIP {
|
||||
|
|
|
@ -238,6 +238,7 @@ dns:
|
|||
- https://doh.pub/dns-query
|
||||
- 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
|
||||
- name: "socks"
|
||||
|
|
6
go.mod
6
go.mod
|
@ -51,6 +51,11 @@ require (
|
|||
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 (
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/andybalholm/brotli v1.0.5 // indirect
|
||||
|
@ -67,6 +72,7 @@ require (
|
|||
github.com/mdlayher/socket v0.4.0 // indirect
|
||||
github.com/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03 // 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/pmezard/go-difflib v1.0.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/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
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/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
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/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/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.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
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/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||
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/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc=
|
||||
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/mroth/weightedrand/v2 v2.0.0 h1:ADehnByWbliEDIazDAKFdBHoqgHSXAkgyKqM/9YsPoo=
|
||||
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/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
|
||||
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/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw=
|
||||
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
|
||||
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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
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.6.1/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/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 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.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"net/netip"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/Dreamacro/clash/adapter"
|
||||
|
@ -91,7 +92,7 @@ func ApplyConfig(cfg *config.Config, force bool) {
|
|||
updateSniffer(cfg.Sniffer)
|
||||
updateHosts(cfg.Hosts)
|
||||
updateGeneral(cfg.General)
|
||||
updateDNS(cfg.DNS, cfg.General.IPv6)
|
||||
updateDNS(cfg.DNS, cfg.RuleProviders, cfg.General.IPv6)
|
||||
updateListeners(cfg.General, cfg.Listeners, force)
|
||||
updateIPTables(cfg)
|
||||
updateTun(cfg.General)
|
||||
|
@ -104,7 +105,7 @@ func ApplyConfig(cfg *config.Config, force bool) {
|
|||
loadProxyProvider(cfg.Providers)
|
||||
updateProfile(cfg)
|
||||
loadRuleProvider(cfg.RuleProviders)
|
||||
|
||||
runtime.GC()
|
||||
tunnel.OnRunning()
|
||||
|
||||
log.SetLevel(cfg.General.LogLevel)
|
||||
|
@ -175,10 +176,9 @@ func updateListeners(general *config.General, listeners map[string]C.InboundList
|
|||
}
|
||||
|
||||
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 {
|
||||
resolver.DefaultResolver = nil
|
||||
resolver.DefaultHostMapper = nil
|
||||
|
@ -186,7 +186,25 @@ func updateDNS(c *config.DNS, generalIPv6 bool) {
|
|||
dns.ReCreateServer("", nil, nil)
|
||||
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{
|
||||
Main: c.NameServer,
|
||||
Fallback: c.Fallback,
|
||||
|
@ -202,9 +220,10 @@ func updateDNS(c *config.DNS, generalIPv6 bool) {
|
|||
Domain: c.FallbackFilter.Domain,
|
||||
GeoSite: c.FallbackFilter.GeoSite,
|
||||
},
|
||||
Default: c.DefaultNameserver,
|
||||
Policy: c.NameServerPolicy,
|
||||
ProxyServer: c.ProxyServerNameserver,
|
||||
Default: c.DefaultNameserver,
|
||||
Policy: c.NameServerPolicy,
|
||||
ProxyServer: c.ProxyServerNameserver,
|
||||
DomainSetPolicy: domainSetPolicies,
|
||||
}
|
||||
|
||||
r := dns.NewResolver(cfg)
|
||||
|
|
|
@ -110,7 +110,10 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.
|
|||
conn2 = nil
|
||||
}()
|
||||
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)
|
||||
if err != nil {
|
||||
buff.Release()
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"golang.org/x/net/idna"
|
||||
"strings"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
|
@ -11,7 +10,6 @@ type Domain struct {
|
|||
*Base
|
||||
domain string
|
||||
adapter string
|
||||
isIDNA bool
|
||||
}
|
||||
|
||||
func (d *Domain) RuleType() C.RuleType {
|
||||
|
@ -27,20 +25,14 @@ func (d *Domain) Adapter() string {
|
|||
}
|
||||
|
||||
func (d *Domain) Payload() string {
|
||||
domain := d.domain
|
||||
if d.isIDNA {
|
||||
domain, _ = idna.ToUnicode(domain)
|
||||
}
|
||||
return domain
|
||||
return d.domain
|
||||
}
|
||||
|
||||
func NewDomain(domain string, adapter string) *Domain {
|
||||
actualDomain, _ := idna.ToASCII(domain)
|
||||
return &Domain{
|
||||
Base: &Base{},
|
||||
domain: strings.ToLower(actualDomain),
|
||||
domain: strings.ToLower(domain),
|
||||
adapter: adapter,
|
||||
isIDNA: actualDomain != domain,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"golang.org/x/net/idna"
|
||||
"strings"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
|
@ -11,7 +10,6 @@ type DomainKeyword struct {
|
|||
*Base
|
||||
keyword string
|
||||
adapter string
|
||||
isIDNA bool
|
||||
}
|
||||
|
||||
func (dk *DomainKeyword) RuleType() C.RuleType {
|
||||
|
@ -28,20 +26,14 @@ func (dk *DomainKeyword) Adapter() string {
|
|||
}
|
||||
|
||||
func (dk *DomainKeyword) Payload() string {
|
||||
keyword := dk.keyword
|
||||
if dk.isIDNA {
|
||||
keyword, _ = idna.ToUnicode(keyword)
|
||||
}
|
||||
return keyword
|
||||
return dk.keyword
|
||||
}
|
||||
|
||||
func NewDomainKeyword(keyword string, adapter string) *DomainKeyword {
|
||||
actualDomainKeyword, _ := idna.ToASCII(keyword)
|
||||
return &DomainKeyword{
|
||||
Base: &Base{},
|
||||
keyword: strings.ToLower(actualDomainKeyword),
|
||||
keyword: strings.ToLower(keyword),
|
||||
adapter: adapter,
|
||||
isIDNA: keyword != actualDomainKeyword,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"golang.org/x/net/idna"
|
||||
"strings"
|
||||
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
|
@ -11,7 +10,6 @@ type DomainSuffix struct {
|
|||
*Base
|
||||
suffix string
|
||||
adapter string
|
||||
isIDNA bool
|
||||
}
|
||||
|
||||
func (ds *DomainSuffix) RuleType() C.RuleType {
|
||||
|
@ -28,20 +26,14 @@ func (ds *DomainSuffix) Adapter() string {
|
|||
}
|
||||
|
||||
func (ds *DomainSuffix) Payload() string {
|
||||
suffix := ds.suffix
|
||||
if ds.isIDNA {
|
||||
suffix, _ = idna.ToUnicode(suffix)
|
||||
}
|
||||
return suffix
|
||||
return ds.suffix
|
||||
}
|
||||
|
||||
func NewDomainSuffix(suffix string, adapter string) *DomainSuffix {
|
||||
actualDomainSuffix, _ := idna.ToASCII(suffix)
|
||||
return &DomainSuffix{
|
||||
Base: &Base{},
|
||||
suffix: strings.ToLower(actualDomainSuffix),
|
||||
suffix: strings.ToLower(suffix),
|
||||
adapter: adapter,
|
||||
isIDNA: suffix != actualDomainSuffix,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,10 +21,8 @@ func NewNetworkType(network, adapter string) (*NetworkType, error) {
|
|||
switch strings.ToUpper(network) {
|
||||
case "TCP":
|
||||
ntType.network = C.TCP
|
||||
break
|
||||
case "UDP":
|
||||
ntType.network = C.UDP
|
||||
break
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported network type, only TCP/UDP")
|
||||
}
|
||||
|
|
|
@ -3,13 +3,11 @@ package provider
|
|||
import (
|
||||
"github.com/Dreamacro/clash/component/trie"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/log"
|
||||
"golang.org/x/net/idna"
|
||||
)
|
||||
|
||||
type domainStrategy struct {
|
||||
count int
|
||||
domainRules *trie.DomainTrie[struct{}]
|
||||
domainRules *trie.DomainSet
|
||||
}
|
||||
|
||||
func (d *domainStrategy) ShouldFindProcess() bool {
|
||||
|
@ -17,7 +15,7 @@ func (d *domainStrategy) ShouldFindProcess() 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 {
|
||||
|
@ -29,21 +27,9 @@ func (d *domainStrategy) ShouldResolveIP() bool {
|
|||
}
|
||||
|
||||
func (d *domainStrategy) OnUpdate(rules []string) {
|
||||
domainTrie := trie.New[struct{}]()
|
||||
count := 0
|
||||
for _, rule := range rules {
|
||||
actualDomain, _ := idna.ToASCII(rule)
|
||||
err := domainTrie.Insert(actualDomain, struct{}{})
|
||||
if err != nil {
|
||||
log.Warnln("invalid domain:[%s]", rule)
|
||||
} else {
|
||||
count++
|
||||
}
|
||||
}
|
||||
domainTrie.Optimize()
|
||||
|
||||
domainTrie := trie.NewDomainSet(rules)
|
||||
d.domainRules = domainTrie
|
||||
d.count = count
|
||||
d.count = len(rules)
|
||||
}
|
||||
|
||||
func NewDomainStrategy() *domainStrategy {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//go:build linux
|
||||
// +build linux
|
||||
//go:build linux && !no_fake_tcp
|
||||
// +build linux,!no_fake_tcp
|
||||
|
||||
package faketcp
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//go:build !linux
|
||||
// +build !linux
|
||||
//go:build !linux || no_fake_tcp
|
||||
// +build !linux no_fake_tcp
|
||||
|
||||
package faketcp
|
||||
|
||||
|
|
Loading…
Reference in a new issue