From cfd03a99c22f7d5129877843874f322554452774 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sat, 1 Apr 2023 11:53:39 +0800 Subject: [PATCH 01/88] feat: `nameserver-policy` support use rule-providers and reduce domain-set memory --- common/pool/buffer_low_memory.go | 15 ++ common/pool/buffer_standard.go | 15 ++ common/pool/pool.go | 12 -- common/utils/strings.go | 9 + component/sniffer/dispatcher.go | 12 +- component/tls/config.go | 20 +- component/trie/domain.go | 26 ++- component/trie/domain_test.go | 20 ++ component/trie/node.go | 12 ++ component/trie/set_test.go | 60 ++++++ component/trie/sskv.go | 178 ++++++++++++++++++ config/config.go | 58 +++--- constant/features/low_memory.go | 5 + constant/features/no_doq.go | 7 - constant/features/no_fake_tcp.go | 7 + constant/features/no_gvisor.go | 7 - constant/features/with_gvisor.go | 7 + dns/resolver.go | 43 ++++- docs/config.yaml | 1 + go.mod | 6 + go.sum | 16 +- hub/executor/executor.go | 35 +++- listener/sing_tun/dns.go | 5 +- rules/common/domain.go | 12 +- rules/common/domain_keyword.go | 12 +- rules/common/domain_suffix.go | 12 +- rules/common/network_type.go | 2 - rules/provider/domain_strategy.go | 22 +-- transport/hysteria/conns/faketcp/tcp_linux.go | 4 +- transport/hysteria/conns/faketcp/tcp_stub.go | 4 +- 30 files changed, 503 insertions(+), 141 deletions(-) create mode 100644 common/pool/buffer_low_memory.go create mode 100644 common/pool/buffer_standard.go create mode 100644 common/utils/strings.go create mode 100644 component/trie/set_test.go create mode 100644 component/trie/sskv.go create mode 100644 constant/features/low_memory.go delete mode 100644 constant/features/no_doq.go create mode 100644 constant/features/no_fake_tcp.go delete mode 100644 constant/features/no_gvisor.go create mode 100644 constant/features/with_gvisor.go diff --git a/common/pool/buffer_low_memory.go b/common/pool/buffer_low_memory.go new file mode 100644 index 00000000..24e18a75 --- /dev/null +++ b/common/pool/buffer_low_memory.go @@ -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 +) diff --git a/common/pool/buffer_standard.go b/common/pool/buffer_standard.go new file mode 100644 index 00000000..ff758700 --- /dev/null +++ b/common/pool/buffer_standard.go @@ -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 +) diff --git a/common/pool/pool.go b/common/pool/pool.go index bee4887f..288ea467 100644 --- a/common/pool/pool.go +++ b/common/pool/pool.go @@ -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) } diff --git a/common/utils/strings.go b/common/utils/strings.go new file mode 100644 index 00000000..5d5ae596 --- /dev/null +++ b/common/utils/strings.go @@ -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) +} diff --git a/component/sniffer/dispatcher.go b/component/sniffer/dispatcher.go index 97d448ce..bf0b1bb3 100644 --- a/component/sniffer/dispatcher.go +++ b/component/sniffer/dispatcher.go @@ -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, diff --git a/component/tls/config.go b/component/tls/config.go index 91b89f1d..b5b56591 100644 --- a/component/tls/config.go +++ b/component/tls/config.go @@ -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 diff --git a/component/trie/domain.go b/component/trie/domain.go index d9463c6e..86e5245a 100644 --- a/component/trie/domain.go +++ b/component/trie/domain.go @@ -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]()} diff --git a/component/trie/domain_test.go b/component/trie/domain_test.go index c54b3d3b..2dfd1c34 100644 --- a/component/trie/domain_test.go +++ b/component/trie/domain_test.go @@ -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) +} diff --git a/component/trie/node.go b/component/trie/node.go index e19b40ac..3aa2bc7d 100644 --- a/component/trie/node.go +++ b/component/trie/node.go @@ -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 } diff --git a/component/trie/set_test.go b/component/trie/set_test.go new file mode 100644 index 00000000..346bb31a --- /dev/null +++ b/component/trie/set_test.go @@ -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")) +} diff --git a/component/trie/sskv.go b/component/trie/sskv.go new file mode 100644 index 00000000..6a661a85 --- /dev/null +++ b/component/trie/sskv.go @@ -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) +} diff --git a/config/config.go b/config/config.go index c407aad5..d2378822 100644 --- a/config/config.go +++ b/config/config.go @@ -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 } diff --git a/constant/features/low_memory.go b/constant/features/low_memory.go new file mode 100644 index 00000000..32d10fa6 --- /dev/null +++ b/constant/features/low_memory.go @@ -0,0 +1,5 @@ +package features + +func init() { + TAGS = append(TAGS, "with_low_memory") +} diff --git a/constant/features/no_doq.go b/constant/features/no_doq.go deleted file mode 100644 index c915272f..00000000 --- a/constant/features/no_doq.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build no_doq - -package features - -func init() { - TAGS = append(TAGS, "no_doq") -} diff --git a/constant/features/no_fake_tcp.go b/constant/features/no_fake_tcp.go new file mode 100644 index 00000000..f536a066 --- /dev/null +++ b/constant/features/no_fake_tcp.go @@ -0,0 +1,7 @@ +//go:build no_fake_tcp + +package features + +func init() { + TAGS = append(TAGS, "no_fake_tcp") +} diff --git a/constant/features/no_gvisor.go b/constant/features/no_gvisor.go deleted file mode 100644 index d0d5391a..00000000 --- a/constant/features/no_gvisor.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build no_gvisor - -package features - -func init() { - TAGS = append(TAGS, "no_gvisor") -} diff --git a/constant/features/with_gvisor.go b/constant/features/with_gvisor.go new file mode 100644 index 00000000..1b3417b3 --- /dev/null +++ b/constant/features/with_gvisor.go @@ -0,0 +1,7 @@ +//go:build with_gvisor + +package features + +func init() { + TAGS = append(TAGS, "with_gvisor") +} diff --git a/dns/resolver.go b/dns/resolver.go index c16aad40..b5a09fd0 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -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 { diff --git a/docs/config.yaml b/docs/config.yaml index 3ef09342..c2acc1d4 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -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" diff --git a/go.mod b/go.mod index c8eb6e10..a5e6220e 100644 --- a/go.mod +++ b/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 diff --git a/go.sum b/go.sum index f56aed7a..fe7a51f4 100644 --- a/go.sum +++ b/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= diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 1b2ec572..8ca844d2 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -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) diff --git a/listener/sing_tun/dns.go b/listener/sing_tun/dns.go index 21dee43c..f2daaf0c 100644 --- a/listener/sing_tun/dns.go +++ b/listener/sing_tun/dns.go @@ -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() diff --git a/rules/common/domain.go b/rules/common/domain.go index 6b3eba22..35a06a70 100644 --- a/rules/common/domain.go +++ b/rules/common/domain.go @@ -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, } } diff --git a/rules/common/domain_keyword.go b/rules/common/domain_keyword.go index 94d2a949..d945f200 100644 --- a/rules/common/domain_keyword.go +++ b/rules/common/domain_keyword.go @@ -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, } } diff --git a/rules/common/domain_suffix.go b/rules/common/domain_suffix.go index 4bdc2e2e..b13036a3 100644 --- a/rules/common/domain_suffix.go +++ b/rules/common/domain_suffix.go @@ -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, } } diff --git a/rules/common/network_type.go b/rules/common/network_type.go index fb6b5077..1184ba89 100644 --- a/rules/common/network_type.go +++ b/rules/common/network_type.go @@ -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") } diff --git a/rules/provider/domain_strategy.go b/rules/provider/domain_strategy.go index add64e76..0b2a5d3c 100644 --- a/rules/provider/domain_strategy.go +++ b/rules/provider/domain_strategy.go @@ -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 { diff --git a/transport/hysteria/conns/faketcp/tcp_linux.go b/transport/hysteria/conns/faketcp/tcp_linux.go index dadb0912..cdee9fda 100644 --- a/transport/hysteria/conns/faketcp/tcp_linux.go +++ b/transport/hysteria/conns/faketcp/tcp_linux.go @@ -1,5 +1,5 @@ -//go:build linux -// +build linux +//go:build linux && !no_fake_tcp +// +build linux,!no_fake_tcp package faketcp diff --git a/transport/hysteria/conns/faketcp/tcp_stub.go b/transport/hysteria/conns/faketcp/tcp_stub.go index 9bc55077..9f9ff97d 100644 --- a/transport/hysteria/conns/faketcp/tcp_stub.go +++ b/transport/hysteria/conns/faketcp/tcp_stub.go @@ -1,5 +1,5 @@ -//go:build !linux -// +build !linux +//go:build !linux || no_fake_tcp +// +build !linux no_fake_tcp package faketcp From 54cad53f5f5480794130ceb7efcc180085d3814e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 1 Apr 2023 12:15:03 +0800 Subject: [PATCH 02/88] chore: DomainSet now build from a DomainTrie --- component/trie/{sskv.go => domain_set.go} | 16 ++++------ .../trie/{set_test.go => domain_set_test.go} | 31 ++++++++++++++----- component/trie/domain_test.go | 29 ++++++++--------- config/config.go | 21 +++++++++++-- rules/provider/domain_strategy.go | 11 +++++-- 5 files changed, 72 insertions(+), 36 deletions(-) rename component/trie/{sskv.go => domain_set.go} (91%) rename component/trie/{set_test.go => domain_set_test.go} (62%) diff --git a/component/trie/sskv.go b/component/trie/domain_set.go similarity index 91% rename from component/trie/sskv.go rename to component/trie/domain_set.go index 6a661a85..ce416e16 100644 --- a/component/trie/sskv.go +++ b/component/trie/domain_set.go @@ -23,20 +23,16 @@ type DomainSet struct { 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{}) { +// NewDomainSet creates a new *DomainSet struct, from a DomainTrie. +func (t *DomainTrie[T]) NewDomainSet() *DomainSet { + reserveDomains := make([]string, 0) + t.Foreach(func(domain string, data T) { 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 + keys := reserveDomains if len(keys) == 0 { return nil } @@ -104,7 +100,7 @@ func (ss *DomainSet) Has(key string) bool { if j == len(key) { if getBit(ss.leaves, nextNodeId) != 0 { return true - }else { + } else { goto RESTART } } diff --git a/component/trie/set_test.go b/component/trie/domain_set_test.go similarity index 62% rename from component/trie/set_test.go rename to component/trie/domain_set_test.go index 346bb31a..090bd495 100644 --- a/component/trie/set_test.go +++ b/component/trie/domain_set_test.go @@ -7,7 +7,8 @@ import ( "github.com/stretchr/testify/assert" ) -func TestDomain(t *testing.T) { +func TestDomainSet(t *testing.T) { + tree := trie.New[struct{}]() domainSet := []string{ "baidu.com", "google.com", @@ -15,14 +16,19 @@ func TestDomain(t *testing.T) { "test.a.net", "test.a.oc", } - set := trie.NewDomainSet(domainSet) + + for _, domain := range domainSet { + assert.NoError(t, tree.Insert(domain, struct{}{})) + } + set := tree.NewDomainSet() 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) { +func TestDomainSetComplexWildcard(t *testing.T) { + tree := trie.New[struct{}]() domainSet := []string{ "+.baidu.com", "+.a.baidu.com", @@ -32,14 +38,19 @@ func TestDomainComplexWildcard(t *testing.T) { "test.a.oc", "www.qq.com", } - set := trie.NewDomainSet(domainSet) + + for _, domain := range domainSet { + assert.NoError(t, tree.Insert(domain, struct{}{})) + } + set := tree.NewDomainSet() 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) { +func TestDomainSetWildcard(t *testing.T) { + tree := trie.New[struct{}]() domainSet := []string{ "*.*.*.baidu.com", "www.baidu.*", @@ -47,14 +58,18 @@ func TestDomainWildcard(t *testing.T) { "*.*.qq.com", "test.*.baidu.com", } - set := trie.NewDomainSet(domainSet) + + for _, domain := range domainSet { + assert.NoError(t, tree.Insert(domain, struct{}{})) + } + set := tree.NewDomainSet() 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.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("www.google.com")) assert.False(t, set.Has("test.qq.com")) assert.False(t, set.Has("test.test.test.qq.com")) } diff --git a/component/trie/domain_test.go b/component/trie/domain_test.go index 2dfd1c34..976055a9 100644 --- a/component/trie/domain_test.go +++ b/component/trie/domain_test.go @@ -1,16 +1,17 @@ -package trie +package trie_test import ( "net/netip" "testing" + "github.com/Dreamacro/clash/component/trie" "github.com/stretchr/testify/assert" ) var localIP = netip.AddrFrom4([4]byte{127, 0, 0, 1}) func TestTrie_Basic(t *testing.T) { - tree := New[netip.Addr]() + tree := trie.New[netip.Addr]() domains := []string{ "example.com", "google.com", @@ -18,7 +19,7 @@ func TestTrie_Basic(t *testing.T) { } for _, domain := range domains { - tree.Insert(domain, localIP) + assert.NoError(t, tree.Insert(domain, localIP)) } node := tree.Search("example.com") @@ -31,7 +32,7 @@ func TestTrie_Basic(t *testing.T) { } func TestTrie_Wildcard(t *testing.T) { - tree := New[netip.Addr]() + tree := trie.New[netip.Addr]() domains := []string{ "*.example.com", "sub.*.example.com", @@ -47,7 +48,7 @@ func TestTrie_Wildcard(t *testing.T) { } for _, domain := range domains { - tree.Insert(domain, localIP) + assert.NoError(t, tree.Insert(domain, localIP)) } assert.NotNil(t, tree.Search("sub.example.com")) @@ -64,7 +65,7 @@ func TestTrie_Wildcard(t *testing.T) { } func TestTrie_Priority(t *testing.T) { - tree := New[int]() + tree := trie.New[int]() domains := []string{ ".dev", "example.dev", @@ -79,7 +80,7 @@ func TestTrie_Priority(t *testing.T) { } for idx, domain := range domains { - tree.Insert(domain, idx+1) + assert.NoError(t, tree.Insert(domain, idx+1)) } assertFn("test.dev", 1) @@ -90,8 +91,8 @@ func TestTrie_Priority(t *testing.T) { } func TestTrie_Boundary(t *testing.T) { - tree := New[netip.Addr]() - tree.Insert("*.dev", localIP) + tree := trie.New[netip.Addr]() + assert.NoError(t, tree.Insert("*.dev", localIP)) assert.NotNil(t, tree.Insert(".", localIP)) assert.NotNil(t, tree.Insert("..dev", localIP)) @@ -99,15 +100,15 @@ func TestTrie_Boundary(t *testing.T) { } func TestTrie_WildcardBoundary(t *testing.T) { - tree := New[netip.Addr]() - tree.Insert("+.*", localIP) - tree.Insert("stun.*.*.*", localIP) + tree := trie.New[netip.Addr]() + assert.NoError(t, tree.Insert("+.*", localIP)) + assert.NoError(t, tree.Insert("stun.*.*.*", localIP)) assert.NotNil(t, tree.Search("example.com")) } func TestTrie_Foreach(t *testing.T) { - tree := New[netip.Addr]() + tree := trie.New[netip.Addr]() domainList := []string{ "google.com", "stun.*.*.*", @@ -117,7 +118,7 @@ func TestTrie_Foreach(t *testing.T) { "*.*.baidu.com", } for _, domain := range domainList { - tree.Insert(domain, localIP) + assert.NoError(t, tree.Insert(domain, localIP)) } count := 0 tree.Foreach(func(domain string, data netip.Addr) { diff --git a/config/config.go b/config/config.go index d2378822..e7720c40 100644 --- a/config/config.go +++ b/config/config.go @@ -1340,8 +1340,25 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) { } sniffer.Sniffers = loadSniffer - sniffer.ForceDomain = trie.NewDomainSet(snifferRaw.ForceDomain) - sniffer.SkipDomain = trie.NewDomainSet(snifferRaw.SkipDomain) + + forceDomainTrie := trie.New[struct{}]() + for _, domain := range snifferRaw.ForceDomain { + err := forceDomainTrie.Insert(domain, struct{}{}) + if err != nil { + return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err) + } + } + sniffer.ForceDomain = forceDomainTrie.NewDomainSet() + + skipDomainTrie := trie.New[struct{}]() + for _, domain := range snifferRaw.SkipDomain { + err := skipDomainTrie.Insert(domain, struct{}{}) + if err != nil { + return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err) + } + } + sniffer.SkipDomain = skipDomainTrie.NewDomainSet() + return sniffer, nil } diff --git a/rules/provider/domain_strategy.go b/rules/provider/domain_strategy.go index 0b2a5d3c..a2cb795d 100644 --- a/rules/provider/domain_strategy.go +++ b/rules/provider/domain_strategy.go @@ -3,6 +3,7 @@ package provider import ( "github.com/Dreamacro/clash/component/trie" C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/log" ) type domainStrategy struct { @@ -27,8 +28,14 @@ func (d *domainStrategy) ShouldResolveIP() bool { } func (d *domainStrategy) OnUpdate(rules []string) { - domainTrie := trie.NewDomainSet(rules) - d.domainRules = domainTrie + domainTrie := trie.New[struct{}]() + for _, rule := range rules { + err := domainTrie.Insert(rule, struct{}{}) + if err != nil { + log.Warnln("invalid domain:[%s]", rule) + } + } + d.domainRules = domainTrie.NewDomainSet() d.count = len(rules) } From 54c2fa98b47d199ca83c3708b90197a6b006eb58 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 1 Apr 2023 14:11:09 +0800 Subject: [PATCH 03/88] chore: rule-provider now read yaml line-by-line --- rules/provider/classical_strategy.go | 48 +++++++------ rules/provider/domain_strategy.go | 33 +++++---- rules/provider/ipcidr_strategy.go | 31 ++++---- rules/provider/provider.go | 102 ++++++++++++++++++++++----- 4 files changed, 148 insertions(+), 66 deletions(-) diff --git a/rules/provider/classical_strategy.go b/rules/provider/classical_strategy.go index 25360ec7..e187e213 100644 --- a/rules/provider/classical_strategy.go +++ b/rules/provider/classical_strategy.go @@ -37,36 +37,38 @@ func (c *classicalStrategy) ShouldFindProcess() bool { return c.shouldFindProcess } -func (c *classicalStrategy) OnUpdate(rules []string) { - var classicalRules []C.Rule - shouldResolveIP := false - for _, rawRule := range rules { - ruleType, rule, params := ruleParse(rawRule) +func (c *classicalStrategy) Reset() { + c.rules = nil + c.count = 0 + c.shouldFindProcess = false + c.shouldResolveIP = false +} - if ruleType == "PROCESS-NAME" { +func (c *classicalStrategy) Insert(rule string) { + ruleType, rule, params := ruleParse(rule) + + if ruleType == "PROCESS-NAME" { + c.shouldFindProcess = true + } + + r, err := c.parse(ruleType, rule, "", params) + if err != nil { + log.Warnln("parse rule error:[%s]", err.Error()) + } else { + if r.ShouldResolveIP() { + c.shouldResolveIP = true + } + if r.ShouldFindProcess() { c.shouldFindProcess = true } - r, err := c.parse(ruleType, rule, "", params) - if err != nil { - log.Warnln("parse rule error:[%s]", err.Error()) - } else { - if !shouldResolveIP { - shouldResolveIP = r.ShouldResolveIP() - } - - if !c.shouldFindProcess { - c.shouldFindProcess = r.ShouldFindProcess() - } - - classicalRules = append(classicalRules, r) - } + c.rules = append(c.rules, r) + c.count++ } - - c.rules = classicalRules - c.count = len(classicalRules) } +func (c *classicalStrategy) FinishInsert() {} + func ruleParse(ruleRaw string) (string, string, []string) { item := strings.Split(ruleRaw, ",") if len(item) == 1 { diff --git a/rules/provider/domain_strategy.go b/rules/provider/domain_strategy.go index a2cb795d..d686d598 100644 --- a/rules/provider/domain_strategy.go +++ b/rules/provider/domain_strategy.go @@ -7,8 +7,9 @@ import ( ) type domainStrategy struct { - count int - domainRules *trie.DomainSet + count int + domainTrie *trie.DomainTrie[struct{}] + domainSet *trie.DomainSet } func (d *domainStrategy) ShouldFindProcess() bool { @@ -16,7 +17,7 @@ func (d *domainStrategy) ShouldFindProcess() bool { } func (d *domainStrategy) Match(metadata *C.Metadata) bool { - return d.domainRules != nil && d.domainRules.Has(metadata.RuleHost()) + return d.domainSet != nil && d.domainSet.Has(metadata.RuleHost()) } func (d *domainStrategy) Count() int { @@ -27,16 +28,24 @@ func (d *domainStrategy) ShouldResolveIP() bool { return false } -func (d *domainStrategy) OnUpdate(rules []string) { - domainTrie := trie.New[struct{}]() - for _, rule := range rules { - err := domainTrie.Insert(rule, struct{}{}) - if err != nil { - log.Warnln("invalid domain:[%s]", rule) - } +func (d *domainStrategy) Reset() { + d.domainTrie = trie.New[struct{}]() + d.domainSet = nil + d.count = 0 +} + +func (d *domainStrategy) Insert(rule string) { + err := d.domainTrie.Insert(rule, struct{}{}) + if err != nil { + log.Warnln("invalid domain:[%s]", rule) + } else { + d.count++ } - d.domainRules = domainTrie.NewDomainSet() - d.count = len(rules) +} + +func (d *domainStrategy) FinishInsert() { + d.domainSet = d.domainTrie.NewDomainSet() + d.domainTrie = nil } func NewDomainStrategy() *domainStrategy { diff --git a/rules/provider/ipcidr_strategy.go b/rules/provider/ipcidr_strategy.go index 88228301..f54302f1 100644 --- a/rules/provider/ipcidr_strategy.go +++ b/rules/provider/ipcidr_strategy.go @@ -28,23 +28,24 @@ func (i *ipcidrStrategy) ShouldResolveIP() bool { return i.shouldResolveIP } -func (i *ipcidrStrategy) OnUpdate(rules []string) { - ipCidrTrie := trie.NewIpCidrTrie() - count := 0 - for _, rule := range rules { - err := ipCidrTrie.AddIpCidrForString(rule) - if err != nil { - log.Warnln("invalid Ipcidr:[%s]", rule) - } else { - count++ - } - } - - i.trie = ipCidrTrie - i.count = count - i.shouldResolveIP = i.count > 0 +func (i *ipcidrStrategy) Reset() { + i.trie = trie.NewIpCidrTrie() + i.count = 0 + i.shouldResolveIP = false } +func (i *ipcidrStrategy) Insert(rule string) { + err := i.trie.AddIpCidrForString(rule) + if err != nil { + log.Warnln("invalid Ipcidr:[%s]", rule) + } else { + i.shouldResolveIP = true + i.count++ + } +} + +func (i *ipcidrStrategy) FinishInsert() {} + func NewIPCidrStrategy() *ipcidrStrategy { return &ipcidrStrategy{} } diff --git a/rules/provider/provider.go b/rules/provider/provider.go index 175917c2..c46861e1 100644 --- a/rules/provider/provider.go +++ b/rules/provider/provider.go @@ -1,13 +1,19 @@ package provider import ( + "bufio" + "bytes" "encoding/json" + "errors" + "gopkg.in/yaml.v3" + "io" + "runtime" + "time" + + "github.com/Dreamacro/clash/common/pool" "github.com/Dreamacro/clash/component/resource" C "github.com/Dreamacro/clash/constant" P "github.com/Dreamacro/clash/constant/provider" - "gopkg.in/yaml.v3" - "runtime" - "time" ) var ( @@ -29,8 +35,8 @@ type RulePayload struct { key: Domain or IP Cidr value: Rule type or is empty */ - Rules []string `yaml:"payload"` - Rules2 []string `yaml:"rules"` + Payload []string `yaml:"payload"` + Rules []string `yaml:"rules"` } type ruleStrategy interface { @@ -38,7 +44,9 @@ type ruleStrategy interface { Count() int ShouldResolveIP() bool ShouldFindProcess() bool - OnUpdate(rules []string) + Reset() + Insert(rule string) + FinishInsert() } func RuleProviders() map[string]P.RuleProvider { @@ -114,13 +122,12 @@ func NewRuleSetProvider(name string, behavior P.RuleType, interval time.Duration } onUpdate := func(elm interface{}) { - rulesRaw := elm.([]string) - rp.strategy.OnUpdate(rulesRaw) + strategy := elm.(ruleStrategy) + rp.strategy = strategy } - fetcher := resource.NewFetcher(name, interval, vehicle, rulesParse, onUpdate) - rp.Fetcher = fetcher rp.strategy = newStrategy(behavior, parse) + rp.Fetcher = resource.NewFetcher(name, interval, vehicle, func(bytes []byte) (any, error) { return rulesParse(bytes, newStrategy(behavior, parse)) }, onUpdate) wrapper := &RuleSetProvider{ rp, @@ -147,12 +154,75 @@ func newStrategy(behavior P.RuleType, parse func(tp, payload, target string, par } } -func rulesParse(buf []byte) (any, error) { - rulePayload := RulePayload{} - err := yaml.Unmarshal(buf, &rulePayload) - if err != nil { - return nil, err +var ErrNoPayload = errors.New("file must have a `payload` field") + +func rulesParse(buf []byte, strategy ruleStrategy) (any, error) { + strategy.Reset() + + schema := &RulePayload{} + + reader := bufio.NewReader(bytes.NewReader(buf)) + + firstLineBuffer := pool.GetBuffer() + defer pool.PutBuffer(firstLineBuffer) + firstLineLength := 0 + + for { + line, isPrefix, err := reader.ReadLine() + if err != nil { + if err == io.EOF { + if firstLineLength == 0 { // find payload head + return nil, ErrNoPayload + } + break + } + return nil, err + } + firstLineBuffer.Write(line) // need a copy because the returned buffer is only valid until the next call to ReadLine + if isPrefix { + // If the line was too long for the buffer then isPrefix is set and the + // beginning of the line is returned. The rest of the line will be returned + // from future calls. + continue + } + if firstLineLength == 0 { // find payload head + firstLineBuffer.WriteByte('\n') + firstLineLength = firstLineBuffer.Len() + firstLineBuffer.WriteString(" - ''") // a test line + + err = yaml.Unmarshal(firstLineBuffer.Bytes(), schema) + firstLineBuffer.Truncate(firstLineLength) + if err == nil && (len(schema.Rules) > 0 || len(schema.Payload) > 0) { // found + continue + } + + // not found or err!=nil + firstLineBuffer.Truncate(0) + firstLineLength = 0 + continue + } + + // parse payload body + err = yaml.Unmarshal(firstLineBuffer.Bytes(), schema) + firstLineBuffer.Truncate(firstLineLength) + if err != nil { + continue + } + var str string + if len(schema.Rules) > 0 { + str = schema.Rules[0] + } + if len(schema.Payload) > 0 { + str = schema.Payload[0] + } + if str == "" { + continue + } + + strategy.Insert(str) } - return append(rulePayload.Rules, rulePayload.Rules2...), nil + strategy.FinishInsert() + + return strategy, nil } From 9d7a78e1ef301d8faaa37b05838126309903ee73 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sat, 1 Apr 2023 14:32:34 +0800 Subject: [PATCH 04/88] chore: update use compatible version for windows/linux amd64 --- hub/updater/updater.go | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/hub/updater/updater.go b/hub/updater/updater.go index 077818fd..b428ca9c 100644 --- a/hub/updater/updater.go +++ b/hub/updater/updater.go @@ -67,7 +67,7 @@ func Update() (err error) { return err } - log.Infoln("current version alpha-%s, latest version alpha-%s", constant.Version, latestVersion) + log.Infoln("current version %s, latest version %s", constant.Version, latestVersion) if latestVersion == constant.Version { err := &updateError{Message: "Already using latest version"} @@ -134,8 +134,10 @@ func prepare(exePath string) (err error) { //log.Infoln(packageName) backupDir = filepath.Join(workDir, "meta-backup") - if goos == "windows" { - updateExeName = "clash.meta" + "-" + goos + "-" + goarch + ".exe" + if goos == "windows" && goarch == "amd64" { + updateExeName = "clash.meta" + "-" + goos + "-" + goarch + "-compatible.exe" + } else if goos == "linux" && goarch == "amd64" { + updateExeName = "clash.meta" + "-" + goos + "-" + goarch + "-compatible" } else { updateExeName = "clash.meta" + "-" + goos + "-" + goarch } @@ -164,7 +166,7 @@ func unpack() error { var err error _, pkgNameOnly := filepath.Split(packageURL) - log.Debugln("updater: unpacking package") + log.Infoln("updater: unpacking package") if strings.HasSuffix(pkgNameOnly, ".zip") { _, err = zipFileUnpack(packageName, updateDir) if err != nil { @@ -184,15 +186,14 @@ func unpack() error { return nil } -// backup makes a backup of the current configuration and supporting files. It -// ignores the configuration file if firstRun is true. +// backup makes a backup of the current configuration and supporting files. func backup() (err error) { - log.Infoln("updater: backing up current Exefile") + log.Infoln("updater: backing up current ExecFile:%s", currentExeName) _ = os.Mkdir(backupDir, 0o755) - err = copyFile(currentExeName, backupExeName) + err = os.Rename(currentExeName, backupExeName) if err != nil { - return fmt.Errorf("copySupportingFiles(%s, %s) failed: %w", currentExeName, backupExeName, err) + return err } return nil @@ -203,24 +204,17 @@ func backup() (err error) { func replace() error { var err error - // log.Infoln("updater: renaming: %s to %s", currentExeName, backupExeName) - // err := os.Rename(currentExeName, backupExeName) - // if err != nil { - // return err - // } - + log.Infoln("copying: %s to %s", updateExeName, currentExeName) if goos == "windows" { // rename fails with "File in use" error - log.Infoln("copying: %s to %s", updateExeName, currentExeName) err = copyFile(updateExeName, currentExeName) } else { - log.Infoln("copying: %s to %s", updateExeName, currentExeName) err = os.Rename(updateExeName, currentExeName) } if err != nil { return err } - + log.Infoln("updater: renamed: %s to %s", updateExeName, currentExeName) return nil } @@ -236,8 +230,6 @@ const MaxPackageFileSize = 32 * 1024 * 1024 // Download package file and save it to disk func downloadPackageFile() (err error) { - // var resp *http.Response - // resp, err = client.Get(packageURL) ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() resp, err := clashHttp.HttpRequest(ctx, packageURL, http.MethodGet, http.Header{"User-Agent": {"clash"}}, nil) @@ -447,6 +439,8 @@ func updateDownloadURL() { middle = fmt.Sprintf("-%s-%sv%s-%s", goos, goarch, goarm, latestVersion) } else if isMIPS(goarch) && gomips != "" { middle = fmt.Sprintf("-%s-%s-%s-%s", goos, goarch, gomips, latestVersion) + } else if goarch == "amd64" && (goos == "windows" || goos == "linux") { + middle = fmt.Sprintf("-%s-%s-compatible-%s", goos, goarch, latestVersion) } else { middle = fmt.Sprintf("-%s-%s-%s", goos, goarch, latestVersion) } From 9442880a5af379ba3067a6ff0e40ddf87a082cd9 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 1 Apr 2023 16:55:02 +0800 Subject: [PATCH 05/88] chore: rule-provider direct using IndexByte in bytes for find new line --- rules/provider/provider.go | 38 +++++++++++++++----------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/rules/provider/provider.go b/rules/provider/provider.go index c46861e1..6b3c0290 100644 --- a/rules/provider/provider.go +++ b/rules/provider/provider.go @@ -1,12 +1,10 @@ package provider import ( - "bufio" "bytes" "encoding/json" "errors" "gopkg.in/yaml.v3" - "io" "runtime" "time" @@ -161,36 +159,30 @@ func rulesParse(buf []byte, strategy ruleStrategy) (any, error) { schema := &RulePayload{} - reader := bufio.NewReader(bytes.NewReader(buf)) - firstLineBuffer := pool.GetBuffer() defer pool.PutBuffer(firstLineBuffer) firstLineLength := 0 - for { - line, isPrefix, err := reader.ReadLine() - if err != nil { - if err == io.EOF { - if firstLineLength == 0 { // find payload head - return nil, ErrNoPayload - } - break + s := 0 // search start index + for s < len(buf) { + // search buffer for a new line. + line := buf[s:] + if i := bytes.IndexByte(line, '\n'); i >= 0 { + i += s + line = buf[s : i+1] + s = i + 1 + } else { + s = len(buf) // stop loop in next step + if firstLineLength == 0 { // no head or only one line body + return nil, ErrNoPayload } - return nil, err - } - firstLineBuffer.Write(line) // need a copy because the returned buffer is only valid until the next call to ReadLine - if isPrefix { - // If the line was too long for the buffer then isPrefix is set and the - // beginning of the line is returned. The rest of the line will be returned - // from future calls. - continue } + firstLineBuffer.Write(line) if firstLineLength == 0 { // find payload head - firstLineBuffer.WriteByte('\n') firstLineLength = firstLineBuffer.Len() firstLineBuffer.WriteString(" - ''") // a test line - err = yaml.Unmarshal(firstLineBuffer.Bytes(), schema) + err := yaml.Unmarshal(firstLineBuffer.Bytes(), schema) firstLineBuffer.Truncate(firstLineLength) if err == nil && (len(schema.Rules) > 0 || len(schema.Payload) > 0) { // found continue @@ -203,7 +195,7 @@ func rulesParse(buf []byte, strategy ruleStrategy) (any, error) { } // parse payload body - err = yaml.Unmarshal(firstLineBuffer.Bytes(), schema) + err := yaml.Unmarshal(firstLineBuffer.Bytes(), schema) firstLineBuffer.Truncate(firstLineLength) if err != nil { continue From 4af4935e7edebed6a8f799643d734fa81909e10f Mon Sep 17 00:00:00 2001 From: Hellojack <106379370+h1jk@users.noreply.github.com> Date: Sat, 1 Apr 2023 20:08:49 +0800 Subject: [PATCH 06/88] fix: Vision slice out of bounds error --- transport/vless/filter.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/transport/vless/filter.go b/transport/vless/filter.go index 3ddfb8b9..f577be7a 100644 --- a/transport/vless/filter.go +++ b/transport/vless/filter.go @@ -34,7 +34,7 @@ func (vc *Conn) FilterTLS(buffer []byte) (index int) { lenP := len(buffer) vc.packetsToFilter-- if index = bytes.Index(buffer, tlsServerHandshakeStart); index != -1 { - if lenP >= index+5 { + if lenP > index+5 { if buffer[0] == 22 && buffer[1] == 3 && buffer[2] == 3 { vc.isTLS = true if buffer[5] == tlsHandshakeTypeServerHello { @@ -49,7 +49,7 @@ func (vc *Conn) FilterTLS(buffer []byte) (index int) { } } } else if index = bytes.Index(buffer, tlsClientHandshakeStart); index != -1 { - if lenP >= index+5 && buffer[index+5] == tlsHandshakeTypeClientHello { + if lenP > index+5 && buffer[index+5] == tlsHandshakeTypeClientHello { vc.isTLS = true } } From cd95cf48493074e1ed4219f19af16d6c5b9498c4 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 1 Apr 2023 20:56:49 +0800 Subject: [PATCH 07/88] fix: firstWriteCallBackConn can pass N.ExtendedConn too --- adapter/outboundgroup/fallback.go | 17 ++++---- adapter/outboundgroup/loadbalance.go | 17 ++++---- adapter/outboundgroup/urltest.go | 17 ++++---- common/callback/callback.go | 62 +++++++++++++++++++++++++--- 4 files changed, 78 insertions(+), 35 deletions(-) diff --git a/adapter/outboundgroup/fallback.go b/adapter/outboundgroup/fallback.go index d1d5e6b3..02ba0ac6 100644 --- a/adapter/outboundgroup/fallback.go +++ b/adapter/outboundgroup/fallback.go @@ -37,16 +37,13 @@ func (f *Fallback) DialContext(ctx context.Context, metadata *C.Metadata, opts . } if N.NeedHandshake(c) { - c = &callback.FirstWriteCallBackConn{ - Conn: c, - Callback: func(err error) { - if err == nil { - f.onDialSuccess() - } else { - f.onDialFailed(proxy.Type(), err) - } - }, - } + c = callback.NewFirstWriteCallBackConn(c, func(err error) { + if err == nil { + f.onDialSuccess() + } else { + f.onDialFailed(proxy.Type(), err) + } + }) } return c, err diff --git a/adapter/outboundgroup/loadbalance.go b/adapter/outboundgroup/loadbalance.go index 1ed80496..15e17a13 100644 --- a/adapter/outboundgroup/loadbalance.go +++ b/adapter/outboundgroup/loadbalance.go @@ -95,16 +95,13 @@ func (lb *LoadBalance) DialContext(ctx context.Context, metadata *C.Metadata, op } if N.NeedHandshake(c) { - c = &callback.FirstWriteCallBackConn{ - Conn: c, - Callback: func(err error) { - if err == nil { - lb.onDialSuccess() - } else { - lb.onDialFailed(proxy.Type(), err) - } - }, - } + c = callback.NewFirstWriteCallBackConn(c, func(err error) { + if err == nil { + lb.onDialSuccess() + } else { + lb.onDialFailed(proxy.Type(), err) + } + }) } return diff --git a/adapter/outboundgroup/urltest.go b/adapter/outboundgroup/urltest.go index d340539c..64ce17f6 100644 --- a/adapter/outboundgroup/urltest.go +++ b/adapter/outboundgroup/urltest.go @@ -45,16 +45,13 @@ func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata, opts .. } if N.NeedHandshake(c) { - c = &callback.FirstWriteCallBackConn{ - Conn: c, - Callback: func(err error) { - if err == nil { - u.onDialSuccess() - } else { - u.onDialFailed(proxy.Type(), err) - } - }, - } + c = callback.NewFirstWriteCallBackConn(c, func(err error) { + if err == nil { + u.onDialSuccess() + } else { + u.onDialFailed(proxy.Type(), err) + } + }) } return c, err diff --git a/common/callback/callback.go b/common/callback/callback.go index a0f1e717..9d64bb92 100644 --- a/common/callback/callback.go +++ b/common/callback/callback.go @@ -1,25 +1,77 @@ package callback import ( + "github.com/Dreamacro/clash/common/buf" + N "github.com/Dreamacro/clash/common/net" C "github.com/Dreamacro/clash/constant" ) -type FirstWriteCallBackConn struct { +type firstWriteCallBackConn struct { C.Conn - Callback func(error) + callback func(error) written bool } -func (c *FirstWriteCallBackConn) Write(b []byte) (n int, err error) { +func (c *firstWriteCallBackConn) Write(b []byte) (n int, err error) { defer func() { if !c.written { c.written = true - c.Callback(err) + c.callback(err) } }() return c.Conn.Write(b) } -func (c *FirstWriteCallBackConn) Upstream() any { +func (c *firstWriteCallBackConn) Upstream() any { return c.Conn } + +type extendedConn interface { + C.Conn + N.ExtendedConn +} + +type firstWriteCallBackExtendedConn struct { + extendedConn + callback func(error) + written bool +} + +func (c *firstWriteCallBackExtendedConn) Write(b []byte) (n int, err error) { + defer func() { + if !c.written { + c.written = true + c.callback(err) + } + }() + return c.extendedConn.Write(b) +} + +func (c *firstWriteCallBackExtendedConn) WriteBuffer(buffer *buf.Buffer) (err error) { + defer func() { + if !c.written { + c.written = true + c.callback(err) + } + }() + return c.extendedConn.WriteBuffer(buffer) +} + +func (c *firstWriteCallBackExtendedConn) Upstream() any { + return c.extendedConn +} + +func NewFirstWriteCallBackConn(c C.Conn, callback func(error)) C.Conn { + if c, ok := c.(extendedConn); ok { + return &firstWriteCallBackExtendedConn{ + extendedConn: c, + callback: callback, + written: false, + } + } + return &firstWriteCallBackConn{ + Conn: c, + callback: callback, + written: false, + } +} From 526ac3906dab9744122668c1fbdffdf09f0e3a9c Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 2 Apr 2023 13:15:41 +0800 Subject: [PATCH 08/88] chore: Update dependencies --- go.mod | 31 +++++++++++++------- go.sum | 93 ++++++++++++++++++++++++---------------------------------- 2 files changed, 59 insertions(+), 65 deletions(-) diff --git a/go.mod b/go.mod index a5e6220e..7b33fcb5 100644 --- a/go.mod +++ b/go.mod @@ -15,19 +15,19 @@ require ( github.com/google/gopacket v1.1.19 github.com/gorilla/websocket v1.5.0 github.com/hashicorp/golang-lru v0.5.4 - github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8 + github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961 github.com/jpillora/backoff v1.0.0 github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 - github.com/metacubex/sing-shadowsocks v0.1.1-0.20230226153717-4e80da7e6947 - github.com/metacubex/sing-tun v0.1.3-0.20230323115055-7935ba0ac8b3 + github.com/metacubex/sing-shadowsocks v0.2.1 + github.com/metacubex/sing-tun v0.1.3 github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb github.com/miekg/dns v1.1.52 github.com/mroth/weightedrand/v2 v2.0.0 github.com/oschwald/geoip2-golang v1.8.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 - github.com/sagernet/sing v0.2.1-0.20230323071235-f8038854d286 + github.com/sagernet/sing v0.2.1 github.com/sagernet/sing-shadowtls v0.1.0 github.com/sagernet/sing-vmess v0.1.3 github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 @@ -40,7 +40,7 @@ require ( github.com/zhangyunhao116/fastrand v0.3.0 go.etcd.io/bbolt v1.3.6 go.uber.org/atomic v1.10.0 - go.uber.org/automaxprocs v1.5.1 + go.uber.org/automaxprocs v1.5.2 golang.org/x/crypto v0.7.0 golang.org/x/exp v0.0.0-20230321023759-10a507213a29 golang.org/x/net v0.8.0 @@ -52,14 +52,15 @@ require ( ) require ( - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect - gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect -) - -require ( + github.com/RyuaNerin/go-krypto v1.0.2 // indirect + github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect github.com/ajg/form v1.5.1 // indirect github.com/andybalholm/brotli v1.0.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 // indirect + github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect + github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect + github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/golang/mock v1.6.0 // indirect @@ -71,20 +72,28 @@ require ( github.com/klauspost/cpuid/v2 v2.0.12 // indirect github.com/mdlayher/socket v0.4.0 // indirect github.com/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03 // indirect + github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect + github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // 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/pierrec/lz4/v4 v4.1.14 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qtls-go1-19 v0.2.1 // indirect github.com/quic-go/qtls-go1-20 v0.1.1 // indirect github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect - github.com/u-root/uio v0.0.0-20221213070652-c3537552635f // indirect + github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect + github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect + github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e // indirect + github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect + gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect golang.org/x/mod v0.8.0 // indirect golang.org/x/text v0.8.0 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect golang.org/x/tools v0.6.0 // indirect + gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect ) replace go.uber.org/atomic v1.10.0 => github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370 diff --git a/go.sum b/go.sum index fe7a51f4..7c60b4bf 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,9 @@ github.com/3andne/restls-client-go v0.1.4 h1:kLNC2aSRHPlEVYmTj6EOqJoorCpobEe2toMRSfBF7FU= github.com/3andne/restls-client-go v0.1.4/go.mod h1:04CGbRk1BwBiEDles8b5mlKgTqIwE5MqF7JDloJV47I= +github.com/RyuaNerin/go-krypto v1.0.2 h1:9KiZrrBs+tDrQ66dNy4nrX6SzntKtSKdm0wKHhdB4WM= +github.com/RyuaNerin/go-krypto v1.0.2/go.mod h1:17LzMeJCgzGTkPH3TmfzRnEJ/yA7ErhTPp9sxIqONtA= +github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 h1:cDVUiFo+npB0ZASqnw4q90ylaVAbnYyx0JYqK4YcGok= +github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344/go.mod h1:9pIqrY6SXNL8vjRQE5Hd/OL5GyK/9MrGUWs87z/eFfk= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= @@ -19,7 +23,15 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo= github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= +github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8= +github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I= +github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g= +github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391/go.mod h1:K2R7GhgxrlJzHw2qiPWsCZXf/kXEJN9PLnQK73Ll0po= +github.com/ericlagergren/saferand v0.0.0-20220206064634-960a4dd2bc5c h1:RUzBDdZ+e/HEe2Nh8lYsduiPAZygUfVXJn0Ncj5sHMg= +github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 h1:tlDMEdcPRQKBEz5nGDMvswiajqh7k8ogWRlhRwKy5mY= +github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1/go.mod h1:4RfsapbGx2j/vU5xC/5/9qB3kn9Awp1YDiEnN43QrJ4= +github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 h1:fuGucgPk5dN6wzfnxl3D0D3rVLw4v2SbBT9jb4VnxzA= +github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010/go.mod h1:JtBcj7sBuTTRupn7c2bFspMDIObMJsVK8TeUvpShPok= github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= @@ -40,11 +52,6 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS 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/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -52,25 +59,19 @@ github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8 h1:Z72DOke2yOK0Ms4Z2LK1E1OrRJXOxSj5DllTz2FYTRg= -github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8/go.mod h1:m5WMe03WCvWcXjRnhvaAbAAXdCnu20J5P+mmH44ZzpE= +github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961 h1:x/YtdDlmypenG1te/FfH6LVM+3krhXk5CFV8VYNNX5M= +github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= -github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= -github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= -github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw= github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= @@ -82,25 +83,18 @@ 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= -github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y= -github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= -github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= -github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= -github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 h1:HSkXG1bE/qcRuuPlZ2Jyf0Od8HLxOowi7CzKQqNtWn4= github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7/go.mod h1:1ztDZHGbU5MjN5lNZpkpG8ygndjjWzcojp/H7r6l6QQ= -github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= -github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= github.com/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03 h1:gREIdurac9fpyBMBRPPMF/Sk3gKfPfdNCa4GQyR9FoA= github.com/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03/go.mod h1:wqEuzdImyqD2MCGE8CYRJXbB77oSEJeoSSXXdwKjnsE= github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 h1:KD96JPdTIayTGGgRl6PuVqo2Bpo6+x3LqDDyqrYDDXw= github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594/go.mod h1:9nOiGX6kqV3+ZbkDKdTNzdFD726QQHPH6WDb36jUSpA= -github.com/metacubex/sing-shadowsocks v0.1.1-0.20230226153717-4e80da7e6947 h1:NnjC2+aIiyzzvFlo+C2WzBOJdsp+HAtu18FZomqYhUE= -github.com/metacubex/sing-shadowsocks v0.1.1-0.20230226153717-4e80da7e6947/go.mod h1:U2gwhxzqgbhKCgn2B4z3t0Cj0LpMWFl/02BGCoG421w= -github.com/metacubex/sing-tun v0.1.3-0.20230323115055-7935ba0ac8b3 h1:LnKcLs0HI0HX4xH/2XerX+1BLXS1Uj6Xvzn20xFuCOk= -github.com/metacubex/sing-tun v0.1.3-0.20230323115055-7935ba0ac8b3/go.mod h1:0i22nk0tgkQz/N96hrhPib1O/C5AjxSnco7Mwi2YSF0= +github.com/metacubex/sing-shadowsocks v0.2.1 h1:tpSZQIzXT/qWDNPZjnb6jnj/htkcESLRJ37SNg2mXy4= +github.com/metacubex/sing-shadowsocks v0.2.1/go.mod h1:PNgGOvhangviWQkUH/spVwVt5w42nvs8jYckRh5WoCM= +github.com/metacubex/sing-tun v0.1.3 h1:LCz8TlAZUWwnBYZxs1PEE04PyfLA9xdsye6CjHZlZGQ= +github.com/metacubex/sing-tun v0.1.3/go.mod h1:kHkYHoRlYA4I12QPTt/ADyRGqy5YweLUfye1E1hC/q4= github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb h1:uhvzbtOvyg2c1k1H2EeVPuPvTEjDHCq4+U0AljG40P8= github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb/go.mod h1:7mPG9qYln+CLKBcDt7Dk4c7b3S53VzEfexMVPe6T6FM= github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370 h1:UkViS4DCESAUEYgbIEQdD02hyMacFt6Dny+1MOJtNIo= @@ -111,6 +105,8 @@ github.com/mroth/weightedrand/v2 v2.0.0 h1:ADehnByWbliEDIazDAKFdBHoqgHSXAkgyKqM/ 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/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 h1:1102pQc2SEPp5+xrS26wEaeb26sZy6k9/ZXlZN+eXE4= +github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7/go.mod h1:UqoUn6cHESlliMhOnKLWr+CBH+e3bazUPvFj1XZwAjs= 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= @@ -123,6 +119,8 @@ github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6 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/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE= +github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= 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= @@ -139,8 +137,8 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= -github.com/sagernet/sing v0.2.1-0.20230323071235-f8038854d286 h1:0Td2b5l1KgrdlOnbRWgFFWsyb0TLoq/tP6j9Lut4JN0= -github.com/sagernet/sing v0.2.1-0.20230323071235-f8038854d286/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw= +github.com/sagernet/sing v0.2.1 h1:r0STYeyfKBBtoAHsBtW1dQonxG+3Qidde7/1VAMhdn8= +github.com/sagernet/sing v0.2.1/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw= github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ= github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc= github.com/sagernet/sing-vmess v0.1.3 h1:q/+tsF46dvvapL6CpQBgPHJ6nQrDUZqEtLHCbsjO7iM= @@ -153,10 +151,14 @@ github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aW github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI= github.com/samber/lo v1.37.0 h1:XjVcB8g6tgUp8rsPsJ2CvhClfImrpL04YpQHXeHPhRw= github.com/samber/lo v1.37.0/go.mod h1:9vaz2O4o8oOnK23pd2TrXufcbdbJIa3b6cstBWKpopA= +github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b h1:rXHg9GrUEtWZhEkrykicdND3VPjlVbYiLdX9J7gimS8= +github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b/go.mod h1:X7qrxNQViEaAN9LNZOPl9PfvQtp3V3c7LTo0dvGi0fM= +github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c h1:DjKMC30y6yjG3IxDaeAj3PCoRr+IsO+bzyT+Se2m2Hk= +github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c/go.mod h1:NV/a66PhhWYVmUMaotlXJ8fIEFB98u+c8l/CQIEFLrU= +github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e h1:ur8uMsPIFG3i4Gi093BQITvwH9znsz2VUZmnmwHvpIo= +github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e/go.mod h1:+e5fBW3bpPyo+3uLo513gIUblc03egGjMM0+5GKbzK8= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 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= @@ -168,8 +170,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/u-root/uio v0.0.0-20221213070652-c3537552635f h1:dpx1PHxYqAnXzbryJrWP1NQLzEjwcVgFLhkknuFQ7ww= -github.com/u-root/uio v0.0.0-20221213070652-c3537552635f/go.mod h1:IogEAUBXDEwX7oR/BMmCctShYs80ql4hF0ySdzGxf7E= +github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA= +github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837 h1:AHhUwwFJGl27E46OpdJHplZkK09m7aETNBNzhT6t15M= @@ -177,13 +179,14 @@ github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837/go.mod h1:YJTRELIWrGxR1s8x github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zhangyunhao116/fastrand v0.3.0 h1:7bwe124xcckPulX6fxtr2lFdO2KQqaefdtbk+mqO/Ig= github.com/zhangyunhao116/fastrand v0.3.0/go.mod h1:0v5KgHho0VE6HU192HnY15de/oDS8UrbBChIFjIhBtc= +gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo= +gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.uber.org/automaxprocs v1.5.1 h1:e1YG66Lrk73dn4qhg8WFSvhF0JuFQF0ERIp4rpuV8Qk= -go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU= +go.uber.org/automaxprocs v1.5.2 h1:2LxUOGiR3O6tw8ui5sZa2LAaHnsviZdVOUZw4fvbnME= +go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= @@ -193,16 +196,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= @@ -211,20 +206,11 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -242,7 +228,6 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= From affc453b6ea2e6d4d280b3a1d8ced0c76be5f9bb Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sun, 2 Apr 2023 15:16:42 +0800 Subject: [PATCH 09/88] chore: better upgrade --- hub/route/restart.go | 34 +++++++++++++++++++--------------- hub/route/upgrade.go | 22 +++++++++++++++++++--- hub/updater/updater.go | 23 +++++++++-------------- 3 files changed, 47 insertions(+), 32 deletions(-) diff --git a/hub/route/restart.go b/hub/route/restart.go index 9539296e..69b4f5b8 100644 --- a/hub/route/restart.go +++ b/hub/route/restart.go @@ -38,25 +38,29 @@ func restart(w http.ResponseWriter, r *http.Request) { // The background context is used because the underlying functions wrap it // with timeout and shut down the server, which handles current request. It // also should be done in a separate goroutine for the same reason. - go func() { - if runtime.GOOS == "windows" { - cmd := exec.Command(execPath, os.Args[1:]...) - log.Infoln("restarting: %q %q", execPath, os.Args[1:]) - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err = cmd.Start() - if err != nil { - log.Fatalln("restarting: %s", err) - } + go runRestart(execPath) +} - os.Exit(0) - } +func runRestart(execPath string) { + var err error + if runtime.GOOS == "windows" { + cmd := exec.Command(execPath, os.Args[1:]...) log.Infoln("restarting: %q %q", execPath, os.Args[1:]) - err = syscall.Exec(execPath, os.Args, os.Environ()) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err = cmd.Start() if err != nil { log.Fatalln("restarting: %s", err) } - }() + + os.Exit(0) + } + + log.Infoln("restarting: %q %q", execPath, os.Args[1:]) + err = syscall.Exec(execPath, os.Args, os.Environ()) + if err != nil { + log.Fatalln("restarting: %s", err) + } } diff --git a/hub/route/upgrade.go b/hub/route/upgrade.go index 5adf79eb..0a7c54f9 100644 --- a/hub/route/upgrade.go +++ b/hub/route/upgrade.go @@ -1,12 +1,15 @@ package route import ( + "fmt" "net/http" + "os" "github.com/Dreamacro/clash/hub/updater" "github.com/Dreamacro/clash/log" "github.com/go-chi/chi/v5" + "github.com/go-chi/render" ) func upgradeRouter() http.Handler { @@ -18,11 +21,24 @@ func upgradeRouter() http.Handler { func upgrade(w http.ResponseWriter, r *http.Request) { // modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L108 log.Infoln("start update") - err := updater.Update() + execPath, err := os.Executable() if err != nil { - log.Errorln("err:%s", err) + render.Status(r, http.StatusInternalServerError) + render.JSON(w, r, newError(fmt.Sprintf("getting path: %s", err))) return } - restart(w, r) + err = updater.Update(execPath) + if err != nil { + render.Status(r, http.StatusInternalServerError) + render.JSON(w, r, newError(fmt.Sprintf("Upgrade: %s", err))) + return + } + + render.JSON(w, r, render.M{"status": "ok"}) + if f, ok := w.(http.Flusher); ok { + f.Flush() + } + + go runRestart(execPath) } diff --git a/hub/updater/updater.go b/hub/updater/updater.go index b428ca9c..3d80707a 100644 --- a/hub/updater/updater.go +++ b/hub/updater/updater.go @@ -50,12 +50,12 @@ type updateError struct { } func (e *updateError) Error() string { - return fmt.Sprintf("error: %s", e.Message) + return fmt.Sprintf("update error: %s", e.Message) } // Update performs the auto-updater. It returns an error if the updater failed. // If firstRun is true, it assumes the configuration file doesn't exist. -func Update() (err error) { +func Update(execPath string) (err error) { mu.Lock() defer mu.Unlock() @@ -63,7 +63,6 @@ func Update() (err error) { goarch = runtime.GOARCH latestVersion, err = getLatestVersion() if err != nil { - err := &updateError{Message: err.Error()} return err } @@ -84,11 +83,6 @@ func Update() (err error) { } }() - execPath, err := os.Executable() - if err != nil { - return fmt.Errorf("getting executable path: %w", err) - } - workDir = filepath.Dir(execPath) err = prepare(execPath) @@ -110,7 +104,7 @@ func Update() (err error) { err = backup() if err != nil { - return fmt.Errorf("replacing: %w", err) + return fmt.Errorf("backuping: %w", err) } err = replace() @@ -186,9 +180,9 @@ func unpack() error { return nil } -// backup makes a backup of the current configuration and supporting files. +// backup makes a backup of the current executable file func backup() (err error) { - log.Infoln("updater: backing up current ExecFile:%s", currentExeName) + log.Infoln("updater: backing up current ExecFile:%s to %s", currentExeName, backupExeName) _ = os.Mkdir(backupDir, 0o755) err = os.Rename(currentExeName, backupExeName) @@ -199,12 +193,11 @@ func backup() (err error) { return nil } -// replace moves the current executable with the updated one and also copies the -// supporting files. +// replace moves the current executable with the updated one func replace() error { var err error - log.Infoln("copying: %s to %s", updateExeName, currentExeName) + log.Infoln("replacing: %s to %s", updateExeName, currentExeName) if goos == "windows" { // rename fails with "File in use" error err = copyFile(updateExeName, currentExeName) @@ -214,7 +207,9 @@ func replace() error { if err != nil { return err } + log.Infoln("updater: renamed: %s to %s", updateExeName, currentExeName) + return nil } From 2ff0f9426272aa963d159bcfb4303640195a871f Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 2 Apr 2023 17:29:17 +0800 Subject: [PATCH 10/88] chore: sync sing-wireguard's update --- adapter/outbound/wireguard.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 7eae30fc..881bdf99 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -105,7 +105,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { reserved[2] = uint8(option.Reserved[2]) } peerAddr := M.ParseSocksaddrHostPort(option.Server, uint16(option.Port)) - outbound.bind = wireguard.NewClientBind(context.Background(), outbound.dialer, peerAddr, reserved) + outbound.bind = wireguard.NewClientBind(context.Background(), outbound.dialer, true, peerAddr, reserved) localPrefixes := make([]netip.Prefix, 0, 2) if len(option.Ip) > 0 { if !strings.Contains(option.Ip, "/") { diff --git a/go.mod b/go.mod index 7b33fcb5..b495a36a 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 github.com/metacubex/sing-shadowsocks v0.2.1 github.com/metacubex/sing-tun v0.1.3 - github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb + github.com/metacubex/sing-wireguard v0.0.0-20230402083957-d134f603ac98 github.com/miekg/dns v1.1.52 github.com/mroth/weightedrand/v2 v2.0.0 github.com/oschwald/geoip2-golang v1.8.0 diff --git a/go.sum b/go.sum index 7c60b4bf..e8667af9 100644 --- a/go.sum +++ b/go.sum @@ -95,8 +95,8 @@ github.com/metacubex/sing-shadowsocks v0.2.1 h1:tpSZQIzXT/qWDNPZjnb6jnj/htkcESLR github.com/metacubex/sing-shadowsocks v0.2.1/go.mod h1:PNgGOvhangviWQkUH/spVwVt5w42nvs8jYckRh5WoCM= github.com/metacubex/sing-tun v0.1.3 h1:LCz8TlAZUWwnBYZxs1PEE04PyfLA9xdsye6CjHZlZGQ= github.com/metacubex/sing-tun v0.1.3/go.mod h1:kHkYHoRlYA4I12QPTt/ADyRGqy5YweLUfye1E1hC/q4= -github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb h1:uhvzbtOvyg2c1k1H2EeVPuPvTEjDHCq4+U0AljG40P8= -github.com/metacubex/sing-wireguard v0.0.0-20230310035749-f7595fcae5cb/go.mod h1:7mPG9qYln+CLKBcDt7Dk4c7b3S53VzEfexMVPe6T6FM= +github.com/metacubex/sing-wireguard v0.0.0-20230402083957-d134f603ac98 h1:Dr6A4drhVkjPaJ5lIf3DwhH2HSo+Qmf8fCQmJoDG5YU= +github.com/metacubex/sing-wireguard v0.0.0-20230402083957-d134f603ac98/go.mod h1:7mPG9qYln+CLKBcDt7Dk4c7b3S53VzEfexMVPe6T6FM= github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370 h1:UkViS4DCESAUEYgbIEQdD02hyMacFt6Dny+1MOJtNIo= github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= github.com/miekg/dns v1.1.52 h1:Bmlc/qsNNULOe6bpXcUTsuOajd0DzRHwup6D9k1An0c= From 99f84b8a66de6bf32b6c3d6d6e725633dece7047 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 2 Apr 2023 22:24:46 +0800 Subject: [PATCH 11/88] chore: make all net.Conn wrapper can pass through N.ExtendedConn --- adapter/outbound/trojan.go | 3 +- adapter/outbound/wireguard.go | 4 +- common/callback/callback.go | 52 ++++++------------------- common/net/refconn.go | 18 ++++++++- constant/adapters.go | 3 +- listener/sing/sing.go | 9 +++-- transport/tuic/congestion/bbr_sender.go | 2 +- transport/tuic/conn.go | 4 +- transport/tuic/server.go | 4 +- tunnel/statistic/tracker.go | 11 ++---- 10 files changed, 45 insertions(+), 65 deletions(-) diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index 030e74a9..4a31538b 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -8,7 +8,6 @@ import ( "net/http" "strconv" - N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/component/dialer" tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" @@ -105,7 +104,7 @@ func (t *Trojan) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) return c, err } err = t.instance.WriteHeader(c, trojan.CommandTCP, serializesSocksAddr(metadata)) - return N.NewExtendedConn(c), err + return c, err } // DialContext implements C.ProxyAdapter diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 881bdf99..f82d120e 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -60,7 +60,7 @@ type wgSingDialer struct { dialer dialer.Dialer } -var _ N.Dialer = &wgSingDialer{} +var _ N.Dialer = (*wgSingDialer)(nil) func (d *wgSingDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { return d.dialer.DialContext(ctx, network, destination.String()) @@ -74,7 +74,7 @@ type wgNetDialer struct { tunDevice wireguard.Device } -var _ dialer.NetDialer = &wgNetDialer{} +var _ dialer.NetDialer = (*wgNetDialer)(nil) func (d wgNetDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { return d.tunDevice.DialContext(ctx, network, M.ParseSocksaddr(address).Unwrap()) diff --git a/common/callback/callback.go b/common/callback/callback.go index 9d64bb92..0bf720f4 100644 --- a/common/callback/callback.go +++ b/common/callback/callback.go @@ -22,53 +22,23 @@ func (c *firstWriteCallBackConn) Write(b []byte) (n int, err error) { return c.Conn.Write(b) } +func (c *firstWriteCallBackConn) WriteBuffer(buffer *buf.Buffer) (err error) { + defer func() { + if !c.written { + c.written = true + c.callback(err) + } + }() + return c.Conn.WriteBuffer(buffer) +} + func (c *firstWriteCallBackConn) Upstream() any { return c.Conn } -type extendedConn interface { - C.Conn - N.ExtendedConn -} - -type firstWriteCallBackExtendedConn struct { - extendedConn - callback func(error) - written bool -} - -func (c *firstWriteCallBackExtendedConn) Write(b []byte) (n int, err error) { - defer func() { - if !c.written { - c.written = true - c.callback(err) - } - }() - return c.extendedConn.Write(b) -} - -func (c *firstWriteCallBackExtendedConn) WriteBuffer(buffer *buf.Buffer) (err error) { - defer func() { - if !c.written { - c.written = true - c.callback(err) - } - }() - return c.extendedConn.WriteBuffer(buffer) -} - -func (c *firstWriteCallBackExtendedConn) Upstream() any { - return c.extendedConn -} +var _ N.ExtendedConn = (*firstWriteCallBackConn)(nil) func NewFirstWriteCallBackConn(c C.Conn, callback func(error)) C.Conn { - if c, ok := c.(extendedConn); ok { - return &firstWriteCallBackExtendedConn{ - extendedConn: c, - callback: callback, - written: false, - } - } return &firstWriteCallBackConn{ Conn: c, callback: callback, diff --git a/common/net/refconn.go b/common/net/refconn.go index 324e6474..59225db0 100644 --- a/common/net/refconn.go +++ b/common/net/refconn.go @@ -4,10 +4,12 @@ import ( "net" "runtime" "time" + + "github.com/Dreamacro/clash/common/buf" ) type refConn struct { - conn net.Conn + conn ExtendedConn ref any } @@ -55,8 +57,20 @@ func (c *refConn) Upstream() any { return c.conn } +func (c *refConn) ReadBuffer(buffer *buf.Buffer) error { + defer runtime.KeepAlive(c.ref) + return c.conn.ReadBuffer(buffer) +} + +func (c *refConn) WriteBuffer(buffer *buf.Buffer) error { + defer runtime.KeepAlive(c.ref) + return c.conn.WriteBuffer(buffer) +} + +var _ ExtendedConn = (*refConn)(nil) + func NewRefConn(conn net.Conn, ref any) net.Conn { - return &refConn{conn: conn, ref: ref} + return &refConn{conn: NewExtendedConn(conn), ref: ref} } type refPacketConn struct { diff --git a/constant/adapters.go b/constant/adapters.go index bf5f7fdb..ce9f9911 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -8,6 +8,7 @@ import ( "sync" "time" + N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/component/dialer" ) @@ -72,7 +73,7 @@ func (c Chain) Last() string { } type Conn interface { - net.Conn + N.ExtendedConn Connection } diff --git a/listener/sing/sing.go b/listener/sing/sing.go index 70462728..2a5a7d50 100644 --- a/listener/sing/sing.go +++ b/listener/sing/sing.go @@ -10,6 +10,7 @@ import ( "time" "github.com/Dreamacro/clash/adapter/inbound" + N "github.com/Dreamacro/clash/common/net" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/transport/socks5" @@ -33,7 +34,7 @@ type ListenerHandler struct { } type waitCloseConn struct { - net.Conn + N.ExtendedConn wg *sync.WaitGroup close sync.Once rAddr net.Addr @@ -43,7 +44,7 @@ func (c *waitCloseConn) Close() error { // call from handleTCPConn(connCtx C.Con c.close.Do(func() { c.wg.Done() }) - return c.Conn.Close() + return c.ExtendedConn.Close() } func (c *waitCloseConn) RemoteAddr() net.Addr { @@ -51,7 +52,7 @@ func (c *waitCloseConn) RemoteAddr() net.Addr { } func (c *waitCloseConn) Upstream() any { - return c.Conn + return c.ExtendedConn } func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { @@ -79,7 +80,7 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta defer wg.Wait() // this goroutine must exit after conn.Close() wg.Add(1) - h.TcpIn <- inbound.NewSocket(target, &waitCloseConn{Conn: conn, wg: wg, rAddr: metadata.Source.TCPAddr()}, h.Type, additions...) + h.TcpIn <- inbound.NewSocket(target, &waitCloseConn{ExtendedConn: N.NewExtendedConn(conn), wg: wg, rAddr: metadata.Source.TCPAddr()}, h.Type, additions...) return nil } diff --git a/transport/tuic/congestion/bbr_sender.go b/transport/tuic/congestion/bbr_sender.go index d848a9a8..99164362 100644 --- a/transport/tuic/congestion/bbr_sender.go +++ b/transport/tuic/congestion/bbr_sender.go @@ -955,7 +955,7 @@ func (b *bbrSender) CalculateRecoveryWindow(ackedBytes, lostBytes congestion.Byt b.recoveryWindow = maxByteCount(b.recoveryWindow, b.minCongestionWindow()) } -var _ congestion.CongestionControl = &bbrSender{} +var _ congestion.CongestionControl = (*bbrSender)(nil) func (b *bbrSender) GetMinRtt() time.Duration { if b.minRtt > 0 { diff --git a/transport/tuic/conn.go b/transport/tuic/conn.go index d5955e13..8f63da75 100644 --- a/transport/tuic/conn.go +++ b/transport/tuic/conn.go @@ -103,7 +103,7 @@ func (q *quicStreamConn) RemoteAddr() net.Addr { return q.rAddr } -var _ net.Conn = &quicStreamConn{} +var _ net.Conn = (*quicStreamConn)(nil) type quicStreamPacketConn struct { connId uint32 @@ -252,4 +252,4 @@ func (q *quicStreamPacketConn) LocalAddr() net.Addr { return q.quicConn.LocalAddr() } -var _ net.PacketConn = &quicStreamPacketConn{} +var _ net.PacketConn = (*quicStreamPacketConn)(nil) diff --git a/transport/tuic/server.go b/transport/tuic/server.go index 5eb6e611..88169aed 100644 --- a/transport/tuic/server.go +++ b/transport/tuic/server.go @@ -294,5 +294,5 @@ func (s *serverUDPPacket) Drop() { s.packet.DATA = nil } -var _ C.UDPPacket = &serverUDPPacket{} -var _ C.UDPPacketInAddr = &serverUDPPacket{} +var _ C.UDPPacket = (*serverUDPPacket)(nil) +var _ C.UDPPacketInAddr = (*serverUDPPacket)(nil) diff --git a/tunnel/statistic/tracker.go b/tunnel/statistic/tracker.go index f7e9d971..11b9c0cd 100644 --- a/tunnel/statistic/tracker.go +++ b/tunnel/statistic/tracker.go @@ -5,7 +5,6 @@ import ( "time" "github.com/Dreamacro/clash/common/buf" - N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/utils" C "github.com/Dreamacro/clash/constant" @@ -32,9 +31,7 @@ type trackerInfo struct { type tcpTracker struct { C.Conn `json:"-"` *trackerInfo - manager *Manager - extendedReader N.ExtendedReader - extendedWriter N.ExtendedWriter + manager *Manager } func (tt *tcpTracker) ID() string { @@ -50,7 +47,7 @@ func (tt *tcpTracker) Read(b []byte) (int, error) { } func (tt *tcpTracker) ReadBuffer(buffer *buf.Buffer) (err error) { - err = tt.extendedReader.ReadBuffer(buffer) + err = tt.Conn.ReadBuffer(buffer) download := int64(buffer.Len()) tt.manager.PushDownloaded(download) tt.DownloadTotal.Add(download) @@ -67,7 +64,7 @@ func (tt *tcpTracker) Write(b []byte) (int, error) { func (tt *tcpTracker) WriteBuffer(buffer *buf.Buffer) (err error) { upload := int64(buffer.Len()) - err = tt.extendedWriter.WriteBuffer(buffer) + err = tt.Conn.WriteBuffer(buffer) tt.manager.PushUploaded(upload) tt.UploadTotal.Add(upload) return @@ -103,8 +100,6 @@ func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.R UploadTotal: atomic.NewInt64(uploadTotal), DownloadTotal: atomic.NewInt64(downloadTotal), }, - extendedReader: N.NewExtendedReader(conn), - extendedWriter: N.NewExtendedWriter(conn), } if rule != nil { From 7308c6c2a9ced1a28689f719ce395522b7ec44cc Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 3 Apr 2023 08:54:28 +0800 Subject: [PATCH 12/88] feat: Add multi-peer support for wireguard outbound --- adapter/outbound/wireguard.go | 220 +++++++++++++++++++++++----------- docs/config.yaml | 13 +- 2 files changed, 162 insertions(+), 71 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index f82d120e..f136a20a 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -41,19 +41,26 @@ type WireGuard struct { type WireGuardOption struct { BasicOption - Name string `proxy:"name"` - Server string `proxy:"server"` - Port int `proxy:"port"` - Ip string `proxy:"ip,omitempty"` - Ipv6 string `proxy:"ipv6,omitempty"` - PrivateKey string `proxy:"private-key"` - PublicKey string `proxy:"public-key"` - PreSharedKey string `proxy:"pre-shared-key,omitempty"` - Reserved []uint8 `proxy:"reserved,omitempty"` - Workers int `proxy:"workers,omitempty"` - MTU int `proxy:"mtu,omitempty"` - UDP bool `proxy:"udp,omitempty"` - PersistentKeepalive int `proxy:"persistent-keepalive,omitempty"` + WireGuardPeerOption + Name string `proxy:"name"` + PrivateKey string `proxy:"private-key"` + Workers int `proxy:"workers,omitempty"` + MTU int `proxy:"mtu,omitempty"` + UDP bool `proxy:"udp,omitempty"` + PersistentKeepalive int `proxy:"persistent-keepalive,omitempty"` + + Peers []WireGuardPeerOption `proxy:"peers,omitempty"` +} + +type WireGuardPeerOption struct { + Server string `proxy:"server"` + Port int `proxy:"port"` + Ip string `proxy:"ip,omitempty"` + Ipv6 string `proxy:"ipv6,omitempty"` + PublicKey string `proxy:"public-key,omitempty"` + PreSharedKey string `proxy:"pre-shared-key,omitempty"` + Reserved []uint8 `proxy:"reserved,omitempty"` + AllowedIPs []string `proxy:"allowed_ips,omitempty"` } type wgSingDialer struct { @@ -80,32 +87,11 @@ func (d wgNetDialer) DialContext(ctx context.Context, network, address string) ( return d.tunDevice.DialContext(ctx, network, M.ParseSocksaddr(address).Unwrap()) } -func NewWireGuard(option WireGuardOption) (*WireGuard, error) { - outbound := &WireGuard{ - Base: &Base{ - name: option.Name, - addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), - tp: C.WireGuard, - udp: option.UDP, - iface: option.Interface, - rmark: option.RoutingMark, - prefer: C.NewDNSPrefer(option.IPVersion), - }, - dialer: &wgSingDialer{dialer: dialer.NewDialer()}, - } - runtime.SetFinalizer(outbound, closeWireGuard) +func (option WireGuardPeerOption) Addr() M.Socksaddr { + return M.ParseSocksaddrHostPort(option.Server, uint16(option.Port)) +} - var reserved [3]uint8 - if len(option.Reserved) > 0 { - if len(option.Reserved) != 3 { - return nil, E.New("invalid reserved value, required 3 bytes, got ", len(option.Reserved)) - } - reserved[0] = uint8(option.Reserved[0]) - reserved[1] = uint8(option.Reserved[1]) - reserved[2] = uint8(option.Reserved[2]) - } - peerAddr := M.ParseSocksaddrHostPort(option.Server, uint16(option.Port)) - outbound.bind = wireguard.NewClientBind(context.Background(), outbound.dialer, true, peerAddr, reserved) +func (option WireGuardPeerOption) Prefixes() ([]netip.Prefix, error) { localPrefixes := make([]netip.Prefix, 0, 2) if len(option.Ip) > 0 { if !strings.Contains(option.Ip, "/") { @@ -130,7 +116,46 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { if len(localPrefixes) == 0 { return nil, E.New("missing local address") } - var privateKey, peerPublicKey, preSharedKey string + return localPrefixes, nil +} + +func NewWireGuard(option WireGuardOption) (*WireGuard, error) { + outbound := &WireGuard{ + Base: &Base{ + name: option.Name, + addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), + tp: C.WireGuard, + udp: option.UDP, + iface: option.Interface, + rmark: option.RoutingMark, + prefer: C.NewDNSPrefer(option.IPVersion), + }, + dialer: &wgSingDialer{dialer: dialer.NewDialer()}, + } + runtime.SetFinalizer(outbound, closeWireGuard) + + var reserved [3]uint8 + if len(option.Reserved) > 0 { + if len(option.Reserved) != 3 { + return nil, E.New("invalid reserved value, required 3 bytes, got ", len(option.Reserved)) + } + copy(reserved[:], option.Reserved) + } + var isConnect bool + var connectAddr M.Socksaddr + if len(option.Peers) < 2 { + isConnect = true + if len(option.Peers) == 1 { + connectAddr = option.Peers[0].Addr() + } else { + connectAddr = option.Addr() + } + } + outbound.bind = wireguard.NewClientBind(context.Background(), outbound.dialer, isConnect, connectAddr, reserved) + + var localPrefixes []netip.Prefix + + var privateKey string { bytes, err := base64.StdEncoding.DecodeString(option.PrivateKey) if err != nil { @@ -138,40 +163,92 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { } privateKey = hex.EncodeToString(bytes) } - { - bytes, err := base64.StdEncoding.DecodeString(option.PublicKey) - if err != nil { - return nil, E.Cause(err, "decode peer public key") - } - peerPublicKey = hex.EncodeToString(bytes) - } - if option.PreSharedKey != "" { - bytes, err := base64.StdEncoding.DecodeString(option.PreSharedKey) - if err != nil { - return nil, E.Cause(err, "decode pre shared key") - } - preSharedKey = hex.EncodeToString(bytes) - } ipcConf := "private_key=" + privateKey - ipcConf += "\npublic_key=" + peerPublicKey - ipcConf += "\nendpoint=" + peerAddr.String() - if preSharedKey != "" { - ipcConf += "\npreshared_key=" + preSharedKey - } - var has4, has6 bool - for _, address := range localPrefixes { - if address.Addr().Is4() { - has4 = true - } else { - has6 = true + if peersLen := len(option.Peers); peersLen > 0 { + localPrefixes = make([]netip.Prefix, 0, peersLen*2) + for i, peer := range option.Peers { + var peerPublicKey, preSharedKey string + { + bytes, err := base64.StdEncoding.DecodeString(peer.PublicKey) + if err != nil { + return nil, E.Cause(err, "decode public key for peer ", i) + } + peerPublicKey = hex.EncodeToString(bytes) + } + if peer.PreSharedKey != "" { + bytes, err := base64.StdEncoding.DecodeString(peer.PreSharedKey) + if err != nil { + return nil, E.Cause(err, "decode pre shared key for peer ", i) + } + preSharedKey = hex.EncodeToString(bytes) + } + destination := peer.Addr() + ipcConf += "\npublic_key=" + peerPublicKey + ipcConf += "\nendpoint=" + destination.String() + if preSharedKey != "" { + ipcConf += "\npreshared_key=" + preSharedKey + } + if len(peer.AllowedIPs) == 0 { + return nil, E.New("missing allowed_ips for peer ", i) + } + for _, allowedIP := range peer.AllowedIPs { + ipcConf += "\nallowed_ip=" + allowedIP + } + if len(peer.Reserved) > 0 { + if len(peer.Reserved) != 3 { + return nil, E.New("invalid reserved value for peer ", i, ", required 3 bytes, got ", len(peer.Reserved)) + } + copy(reserved[:], option.Reserved) + outbound.bind.SetReservedForEndpoint(destination, reserved) + } + prefixes, err := peer.Prefixes() + if err != nil { + return nil, err + } + localPrefixes = append(localPrefixes, prefixes...) + } + } else { + var peerPublicKey, preSharedKey string + { + bytes, err := base64.StdEncoding.DecodeString(option.PublicKey) + if err != nil { + return nil, E.Cause(err, "decode peer public key") + } + peerPublicKey = hex.EncodeToString(bytes) + } + if option.PreSharedKey != "" { + bytes, err := base64.StdEncoding.DecodeString(option.PreSharedKey) + if err != nil { + return nil, E.Cause(err, "decode pre shared key") + } + preSharedKey = hex.EncodeToString(bytes) + } + ipcConf += "\npublic_key=" + peerPublicKey + ipcConf += "\nendpoint=" + connectAddr.String() + if preSharedKey != "" { + ipcConf += "\npreshared_key=" + preSharedKey + } + var err error + localPrefixes, err = option.Prefixes() + if err != nil { + return nil, err + } + var has4, has6 bool + for _, address := range localPrefixes { + if address.Addr().Is4() { + has4 = true + } else { + has6 = true + } + } + if has4 { + ipcConf += "\nallowed_ip=0.0.0.0/0" + } + if has6 { + ipcConf += "\nallowed_ip=::/0" } } - if has4 { - ipcConf += "\nallowed_ip=0.0.0.0/0" - } - if has6 { - ipcConf += "\nallowed_ip=::/0" - } + if option.PersistentKeepalive != 0 { ipcConf += fmt.Sprintf("\npersistent_keepalive_interval=%d", option.PersistentKeepalive) } @@ -179,6 +256,9 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { if mtu == 0 { mtu = 1408 } + if len(localPrefixes) == 0 { + return nil, E.New("missing local address") + } var err error outbound.tunDevice, err = wireguard.NewStackDevice(localPrefixes, uint32(mtu)) if err != nil { diff --git a/docs/config.yaml b/docs/config.yaml index c2acc1d4..32665aaf 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -620,12 +620,23 @@ proxies: # socks5 port: 2480 ip: 172.16.0.2 ipv6: fd01:5ca1:ab1e:80fa:ab85:6eea:213f:f4a5 - private-key: eCtXsJZ27+4PbhDkHnB923tkUn2Gj59wZw5wFA75MnU= public-key: Cr8hWlKvtDt7nrvf+f0brNQQzabAqrjfBvas9pmowjo= + # pre-shared-key: 31aIhAPwktDGpH4JDhA8GNvjFXEf/a6+UaQRyOAiyfM= + private-key: eCtXsJZ27+4PbhDkHnB923tkUn2Gj59wZw5wFA75MnU= udp: true reserved: "U4An" # 数组格式也是合法的 # reserved: [209,98,59] + # 如果peers不为空,该段落中的allowed_ips不可为空;前面段落的server,port,ip,ipv6,public-key,pre-shared-key均会被忽略,但private-key会被保留且只能在顶层指定 + # peers: + # - server: 162.159.192.1 + # port: 2480 + # ip: 172.16.0.2 + # ipv6: fd01:5ca1:ab1e:80fa:ab85:6eea:213f:f4a5 + # public-key: Cr8hWlKvtDt7nrvf+f0brNQQzabAqrjfBvas9pmowjo= + # # pre-shared-key: 31aIhAPwktDGpH4JDhA8GNvjFXEf/a6+UaQRyOAiyfM= + # allowed_ips: ['0.0.0.0/0'] + # reserved: [209,98,59] # tuic - name: tuic From 2fc37501f569203fde44154aa946fc7e14658a5f Mon Sep 17 00:00:00 2001 From: Skyxim Date: Mon, 3 Apr 2023 01:06:04 +0000 Subject: [PATCH 13/88] chore: update demo --- docs/config.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/config.yaml b/docs/config.yaml index 32665aaf..0b179dd4 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -238,7 +238,9 @@ 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 + ## global,dns 为 rule-providers 中的名为 global 和 dns 规则订阅, + ## 且 behavior 必须为 domain/classical,当为 classical 时仅会生效域名类规则 + # "rule-set:global,dns": 8.8.8.8 proxies: # socks5 - name: "socks" From bf31abee22cd235bbedf29499e7156ce51720746 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Mon, 3 Apr 2023 12:03:18 +0000 Subject: [PATCH 14/88] chore: clean up code --- .github/workflows/build.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dc43a10e..87cfb07f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,17 +5,15 @@ on: paths-ignore: - "docs/**" - "README.md" + - ".github/ISSUE_TEMPLATE/**" branches: - Alpha - - Beta - Meta tags: - "v*" pull_request_target: branches: - Alpha - - Beta - - Meta concurrency: group: ${{ github.ref }}-${{ github.workflow }} From ee5c4cb83bfd46994ca9ad1922bb9a1e9803bc46 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 3 Apr 2023 21:07:52 +0800 Subject: [PATCH 15/88] fix: tuic fast-open not work --- transport/tuic/client.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/transport/tuic/client.go b/transport/tuic/client.go index 4932dc9b..af00da03 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -12,6 +12,7 @@ import ( "sync/atomic" "time" + "github.com/Dreamacro/clash/common/buf" N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/pool" C "github.com/Dreamacro/clash/constant" @@ -338,6 +339,14 @@ func (conn *earlyConn) Read(b []byte) (n int, err error) { return conn.BufferedConn.Read(b) } +func (conn *earlyConn) ReadBuffer(buffer *buf.Buffer) (err error) { + err = conn.Response() + if err != nil { + return err + } + return conn.BufferedConn.ReadBuffer(buffer) +} + func (t *clientImpl) ListenPacketWithDialer(ctx context.Context, metadata *C.Metadata, dialer C.Dialer, dialFn DialFunc) (net.PacketConn, error) { quicConn, err := t.getQuicConn(ctx, dialer, dialFn) if err != nil { From ae722bb1a0b965a51a5df2d30f2f6f972a135b03 Mon Sep 17 00:00:00 2001 From: H1JK Date: Wed, 5 Apr 2023 13:51:50 +0800 Subject: [PATCH 16/88] chore: Add early bounds checks --- transport/gun/gun.go | 1 + transport/vmess/websocket.go | 1 + 2 files changed, 2 insertions(+) diff --git a/transport/gun/gun.go b/transport/gun/gun.go index ae2ea6a4..0e5d2321 100644 --- a/transport/gun/gun.go +++ b/transport/gun/gun.go @@ -147,6 +147,7 @@ func (g *Conn) WriteBuffer(buffer *buf.Buffer) error { dataLen := buffer.Len() varLen := UVarintLen(uint64(dataLen)) header := buffer.ExtendHeader(6 + varLen) + _ = header[6] // bounds check hint to compiler header[0] = 0x00 binary.BigEndian.PutUint32(header[1:5], uint32(1+varLen+dataLen)) header[5] = 0x0A diff --git a/transport/vmess/websocket.go b/transport/vmess/websocket.go index 5fcaa0b8..179da886 100644 --- a/transport/vmess/websocket.go +++ b/transport/vmess/websocket.go @@ -107,6 +107,7 @@ func (wsc *websocketConn) WriteBuffer(buffer *buf.Buffer) error { headerLen += 4 // MASK KEY header := buffer.ExtendHeader(headerLen) + _ = header[2] // bounds check hint to compiler header[0] = websocket.BinaryMessage | 1<<7 header[1] = 1 << 7 From f92f34bb207520700418b7213bac50de3b1ac2d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8C=85=E5=B8=83=E4=B8=81?= Date: Sun, 26 Mar 2023 16:22:23 +0800 Subject: [PATCH 17/88] Fix: return pooled buffer when simple-obfs tls read error (#2643) --- transport/simple-obfs/tls.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport/simple-obfs/tls.go b/transport/simple-obfs/tls.go index f41e3263..20166bbe 100644 --- a/transport/simple-obfs/tls.go +++ b/transport/simple-obfs/tls.go @@ -28,10 +28,10 @@ type TLSObfs struct { func (to *TLSObfs) read(b []byte, discardN int) (int, error) { buf := pool.Get(discardN) _, err := io.ReadFull(to.Conn, buf) + pool.Put(buf) if err != nil { return 0, err } - pool.Put(buf) sizeBuf := make([]byte, 2) _, err = io.ReadFull(to.Conn, sizeBuf) From 8ab70d76e7300b73641cd7276ef1d77e35a506b1 Mon Sep 17 00:00:00 2001 From: yaling888 <73897884+yaling888@users.noreply.github.com> Date: Wed, 5 Apr 2023 14:05:23 +0800 Subject: [PATCH 18/88] Fix: should always drop packet when handle UDP packet (#2659) --- tunnel/connection.go | 2 -- tunnel/tunnel.go | 6 ++++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tunnel/connection.go b/tunnel/connection.go index e21bbdbf..c64a5266 100644 --- a/tunnel/connection.go +++ b/tunnel/connection.go @@ -13,8 +13,6 @@ import ( ) func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata) error { - defer packet.Drop() - addr := metadata.UDPAddr() if addr == nil { return errors.New("udp addr invalid") diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index c4f55fbd..e982afa6 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -273,6 +273,7 @@ func resolveMetadata(ctx C.PlainContext, metadata *C.Metadata) (proxy C.Proxy, r func handleUDPConn(packet C.PacketAdapter) { metadata := packet.Metadata() if !metadata.Valid() { + packet.Drop() log.Warnln("[Metadata] not valid: %#v", metadata) return } @@ -284,6 +285,7 @@ func handleUDPConn(packet C.PacketAdapter) { } if err := preHandleMetadata(metadata); err != nil { + packet.Drop() log.Debugln("[Metadata PreHandle] error: %s", err) return } @@ -292,6 +294,7 @@ func handleUDPConn(packet C.PacketAdapter) { if !metadata.Resolved() { ip, err := resolver.ResolveIP(context.Background(), metadata.Host) if err != nil { + packet.Drop() return } metadata.DstIP = ip @@ -309,6 +312,7 @@ func handleUDPConn(packet C.PacketAdapter) { } if handle() { + packet.Drop() return } @@ -316,6 +320,8 @@ func handleUDPConn(packet C.PacketAdapter) { cond, loaded := natTable.GetOrCreateLock(lockKey) go func() { + defer packet.Drop() + if loaded { cond.L.Lock() cond.Wait() From eb07aafce8d0e07304242b756b922dde3e2db40d Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 7 Apr 2023 22:37:01 +0800 Subject: [PATCH 19/88] doc: update config.yaml --- docs/config.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/config.yaml b/docs/config.yaml index 0b179dd4..66eff537 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -680,7 +680,9 @@ proxies: # socks5 # protocol-param: "#" # udp: true -proxy-groups: # 代理链,若落地协议支持 UDP over TCP 则可支持 UDP +proxy-groups: + # 代理链,目前relay可以支持udp的只有vmess/vless/trojan/ss/ssr/tuic + # wireguard目前不支持在relay中使用,请使用tunnels功能模拟 # Traffic: clash <-> http <-> vmess <-> ss1 <-> ss2 <-> Internet - name: "relay" type: relay From 36b35813894641ff102449d0324ce63d7ce15271 Mon Sep 17 00:00:00 2001 From: rookisbusy <129355218+rookisbusy@users.noreply.github.com> Date: Fri, 7 Apr 2023 22:55:01 +0800 Subject: [PATCH 20/88] Alpha (#490) * feat: add memory status for snapshot * feat: add memory status for snapshot --- tunnel/statistic/manager.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tunnel/statistic/manager.go b/tunnel/statistic/manager.go index e67d3871..251450fb 100644 --- a/tunnel/statistic/manager.go +++ b/tunnel/statistic/manager.go @@ -1,6 +1,7 @@ package statistic import ( + "runtime" "sync" "time" @@ -61,10 +62,17 @@ func (m *Manager) Snapshot() *Snapshot { return true }) + getMem := func() uint64 { + var memStats runtime.MemStats + runtime.ReadMemStats(&memStats) + return memStats.StackInuse + memStats.HeapInuse + memStats.HeapIdle - memStats.HeapReleased + } + return &Snapshot{ UploadTotal: m.uploadTotal.Load(), DownloadTotal: m.downloadTotal.Load(), Connections: connections, + Memory: getMem(), } } @@ -92,4 +100,5 @@ type Snapshot struct { DownloadTotal int64 `json:"downloadTotal"` UploadTotal int64 `json:"uploadTotal"` Connections []tracker `json:"connections"` + Memory uint64 `json:"memory"` } From eecd8fff71c3ff1d673110eaa4bf1f27a9c62c05 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 7 Apr 2023 23:53:46 +0800 Subject: [PATCH 21/88] feat: add memory status for snapshot --- go.mod | 10 +++++++++- go.sum | 22 ++++++++++++++++++++++ tunnel/statistic/manager.go | 17 +++++++++++++---- 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index b495a36a..68e3a09b 100644 --- a/go.mod +++ b/go.mod @@ -25,6 +25,7 @@ require ( github.com/metacubex/sing-wireguard v0.0.0-20230402083957-d134f603ac98 github.com/miekg/dns v1.1.52 github.com/mroth/weightedrand/v2 v2.0.0 + github.com/openacid/low v0.1.21 github.com/oschwald/geoip2-golang v1.8.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 github.com/sagernet/sing v0.2.1 @@ -34,6 +35,7 @@ require ( github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c github.com/samber/lo v1.37.0 + github.com/shirou/gopsutil/v3 v3.23.3 github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.2 github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837 @@ -62,6 +64,7 @@ require ( github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/golang/mock v1.6.0 // indirect github.com/google/btree v1.0.1 // indirect @@ -70,24 +73,29 @@ require ( github.com/josharian/native v1.1.0 // indirect github.com/klauspost/compress v1.15.15 // indirect github.com/klauspost/cpuid/v2 v2.0.12 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mdlayher/socket v0.4.0 // indirect github.com/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03 // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // 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/pierrec/lz4/v4 v4.1.14 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qtls-go1-19 v0.2.1 // indirect github.com/quic-go/qtls-go1-20 v0.1.1 // indirect github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect + github.com/shoenig/go-m1cpu v0.1.4 // indirect github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e // indirect + github.com/tklauser/go-sysconf v0.3.11 // indirect + github.com/tklauser/numcpus v0.6.0 // indirect github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect + github.com/yusufpapurcu/wmi v1.2.2 // indirect gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect golang.org/x/mod v0.8.0 // indirect golang.org/x/text v0.8.0 // indirect diff --git a/go.sum b/go.sum index e8667af9..d6320fbe 100644 --- a/go.sum +++ b/go.sum @@ -41,6 +41,8 @@ github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg= github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= @@ -53,6 +55,7 @@ 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/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= @@ -81,6 +84,8 @@ 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/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= 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/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 h1:HSkXG1bE/qcRuuPlZ2Jyf0Od8HLxOowi7CzKQqNtWn4= @@ -124,6 +129,8 @@ github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFu 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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= @@ -151,6 +158,12 @@ github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aW github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI= github.com/samber/lo v1.37.0 h1:XjVcB8g6tgUp8rsPsJ2CvhClfImrpL04YpQHXeHPhRw= github.com/samber/lo v1.37.0/go.mod h1:9vaz2O4o8oOnK23pd2TrXufcbdbJIa3b6cstBWKpopA= +github.com/shirou/gopsutil/v3 v3.23.3 h1:Syt5vVZXUDXPEXpIBt5ziWsJ4LdSAAxF4l/xZeQgSEE= +github.com/shirou/gopsutil/v3 v3.23.3/go.mod h1:lSBNN6t3+D6W5e5nXTxc8KIMMVxAcS+6IJlffjRRlMU= +github.com/shoenig/go-m1cpu v0.1.4 h1:SZPIgRM2sEF9NJy50mRHu9PKGwxyyTTJIWvCtgVbozs= +github.com/shoenig/go-m1cpu v0.1.4/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ= +github.com/shoenig/test v0.6.3 h1:GVXWJFk9PiOjN0KoJ7VrJGH6uLPnqxR7/fe3HUPfE0c= +github.com/shoenig/test v0.6.3/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b h1:rXHg9GrUEtWZhEkrykicdND3VPjlVbYiLdX9J7gimS8= github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b/go.mod h1:X7qrxNQViEaAN9LNZOPl9PfvQtp3V3c7LTo0dvGi0fM= github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c h1:DjKMC30y6yjG3IxDaeAj3PCoRr+IsO+bzyT+Se2m2Hk= @@ -170,6 +183,10 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM= +github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI= +github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms= +github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4= github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA= github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg= @@ -177,6 +194,8 @@ github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1 github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837 h1:AHhUwwFJGl27E46OpdJHplZkK09m7aETNBNzhT6t15M= github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837/go.mod h1:YJTRELIWrGxR1s8xcEBgxcxBfwQfMGjdvNLTjN9XFgY= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= +github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zhangyunhao116/fastrand v0.3.0 h1:7bwe124xcckPulX6fxtr2lFdO2KQqaefdtbk+mqO/Ig= github.com/zhangyunhao116/fastrand v0.3.0/go.mod h1:0v5KgHho0VE6HU192HnY15de/oDS8UrbBChIFjIhBtc= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo= @@ -208,16 +227,19 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/tunnel/statistic/manager.go b/tunnel/statistic/manager.go index 251450fb..09bb3410 100644 --- a/tunnel/statistic/manager.go +++ b/tunnel/statistic/manager.go @@ -1,10 +1,11 @@ package statistic import ( - "runtime" + "os" "sync" "time" + "github.com/shirou/gopsutil/v3/process" "go.uber.org/atomic" ) @@ -18,6 +19,7 @@ func init() { downloadBlip: atomic.NewInt64(0), uploadTotal: atomic.NewInt64(0), downloadTotal: atomic.NewInt64(0), + pid: os.Getpid(), } go DefaultManager.handle() @@ -31,6 +33,7 @@ type Manager struct { downloadBlip *atomic.Int64 uploadTotal *atomic.Int64 downloadTotal *atomic.Int64 + pid int } func (m *Manager) Join(c tracker) { @@ -63,9 +66,15 @@ func (m *Manager) Snapshot() *Snapshot { }) getMem := func() uint64 { - var memStats runtime.MemStats - runtime.ReadMemStats(&memStats) - return memStats.StackInuse + memStats.HeapInuse + memStats.HeapIdle - memStats.HeapReleased + p, err := process.NewProcess(int32(m.pid)) + if err != nil { + return 0 + } + stat, err := p.MemoryInfo() + if err != nil { + return 0 + } + return stat.RSS } return &Snapshot{ From 76340cc99c99adb45597cd194346a70d5f610002 Mon Sep 17 00:00:00 2001 From: rookisbusy Date: Sat, 8 Apr 2023 00:55:25 +0800 Subject: [PATCH 22/88] feat: core support memory chat --- hub/route/server.go | 48 +++++++++++++++++++++++++++++++++++++ tunnel/statistic/manager.go | 8 ++++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/hub/route/server.go b/hub/route/server.go index 848face9..47cb7839 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -39,6 +39,11 @@ type Traffic struct { Down int64 `json:"down"` } +type Memory struct { + Inuse uint64 `json:"inuse"` + OSLimit uint64 `json:"oslimit"` // maybe we need it in the future +} + func SetUIPath(path string) { uiPath = C.Path.Resolve(path) } @@ -76,6 +81,7 @@ func Start(addr string, tlsAddr string, secret string, r.Get("/", hello) r.Get("/logs", getLogs) r.Get("/traffic", traffic) + r.Get("/memory", memory) r.Get("/version", version) r.Mount("/configs", configRouter()) r.Mount("/proxies", proxyRouter()) @@ -224,6 +230,48 @@ func traffic(w http.ResponseWriter, r *http.Request) { } } +func memory(w http.ResponseWriter, r *http.Request) { + var wsConn *websocket.Conn + if websocket.IsWebSocketUpgrade(r) { + var err error + wsConn, err = upgrader.Upgrade(w, r, nil) + if err != nil { + return + } + } + + if wsConn == nil { + w.Header().Set("Content-Type", "application/json") + render.Status(r, http.StatusOK) + } + + tick := time.NewTicker(time.Second) + defer tick.Stop() + t := statistic.DefaultManager + buf := &bytes.Buffer{} + var err error + for range tick.C { + buf.Reset() + + if err := json.NewEncoder(buf).Encode(Memory{ + Inuse: t.Memory(), + OSLimit: 0, + }); err != nil { + break + } + if wsConn == nil { + _, err = w.Write(buf.Bytes()) + w.(http.Flusher).Flush() + } else { + err = wsConn.WriteMessage(websocket.TextMessage, buf.Bytes()) + } + + if err != nil { + break + } + } +} + type Log struct { Type string `json:"type"` Payload string `json:"payload"` diff --git a/tunnel/statistic/manager.go b/tunnel/statistic/manager.go index 09bb3410..7d0ee5e4 100644 --- a/tunnel/statistic/manager.go +++ b/tunnel/statistic/manager.go @@ -34,6 +34,7 @@ type Manager struct { uploadTotal *atomic.Int64 downloadTotal *atomic.Int64 pid int + memory uint64 } func (m *Manager) Join(c tracker) { @@ -58,6 +59,10 @@ func (m *Manager) Now() (up int64, down int64) { return m.uploadBlip.Load(), m.downloadBlip.Load() } +func (m *Manager) Memory() uint64 { + return m.memory +} + func (m *Manager) Snapshot() *Snapshot { connections := []tracker{} m.connections.Range(func(key, value any) bool { @@ -76,12 +81,13 @@ func (m *Manager) Snapshot() *Snapshot { } return stat.RSS } + m.memory = getMem() return &Snapshot{ UploadTotal: m.uploadTotal.Load(), DownloadTotal: m.downloadTotal.Load(), Connections: connections, - Memory: getMem(), + Memory: m.memory, } } From 8fb2c68722898801adbe3933933a488379fc8688 Mon Sep 17 00:00:00 2001 From: rookisbusy Date: Sat, 8 Apr 2023 01:39:48 +0800 Subject: [PATCH 23/88] fix: chat.js not begin with zero --- hub/route/server.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/hub/route/server.go b/hub/route/server.go index 47cb7839..d8124d33 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -250,11 +250,19 @@ func memory(w http.ResponseWriter, r *http.Request) { t := statistic.DefaultManager buf := &bytes.Buffer{} var err error + first := true for range tick.C { buf.Reset() + inuse := t.Memory() + // make chat.js begin with zero + // this is shit var,but we need output 0 for first time + if first { + inuse = 0 + first = false + } if err := json.NewEncoder(buf).Encode(Memory{ - Inuse: t.Memory(), + Inuse: inuse, OSLimit: 0, }); err != nil { break From 2c09ce44f6a439524fcc97a66ea24dd92a17d3cb Mon Sep 17 00:00:00 2001 From: metacubex Date: Sat, 8 Apr 2023 02:04:16 +0800 Subject: [PATCH 24/88] feat: urltest can be select by user --- adapter/outboundgroup/urltest.go | 36 +++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/adapter/outboundgroup/urltest.go b/adapter/outboundgroup/urltest.go index 64ce17f6..f3426a3e 100644 --- a/adapter/outboundgroup/urltest.go +++ b/adapter/outboundgroup/urltest.go @@ -3,6 +3,7 @@ package outboundgroup import ( "context" "encoding/json" + "errors" "time" "github.com/Dreamacro/clash/adapter/outbound" @@ -24,6 +25,8 @@ func urlTestWithTolerance(tolerance uint16) urlTestOption { type URLTest struct { *GroupBase + selected string + testUrl string tolerance uint16 disableUDP bool fastNode C.Proxy @@ -34,6 +37,22 @@ func (u *URLTest) Now() string { return u.fast(false).Name() } +func (u *URLTest) Set(name string) error { + var p C.Proxy + for _, proxy := range u.GetProxies(false) { + if proxy.Name() == name { + p = proxy + break + } + } + if p == nil { + return errors.New("proxy not exist") + } + u.selected = name + u.fast(false) + return nil +} + // DialContext implements C.ProxyAdapter func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (c C.Conn, err error) { proxy := u.fast(true) @@ -74,16 +93,24 @@ func (u *URLTest) Unwrap(metadata *C.Metadata, touch bool) C.Proxy { func (u *URLTest) fast(touch bool) C.Proxy { elm, _, shared := u.fastSingle.Do(func() (C.Proxy, error) { + var s C.Proxy proxies := u.GetProxies(touch) fast := proxies[0] + if fast.Name() == u.selected { + s = fast + } min := fast.LastDelay() fastNotExist := true for _, proxy := range proxies[1:] { + if u.fastNode != nil && proxy.Name() == u.fastNode.Name() { fastNotExist = false } + if proxy.Name() == u.selected { + s = proxy + } if !proxy.Alive() { continue } @@ -94,12 +121,15 @@ func (u *URLTest) fast(touch bool) C.Proxy { min = delay } } - // tolerance if u.fastNode == nil || fastNotExist || !u.fastNode.Alive() || u.fastNode.LastDelay() > fast.LastDelay()+u.tolerance { u.fastNode = fast } - + if s != nil { + if s.Alive() && s.LastDelay() < fast.LastDelay()+u.tolerance { + u.fastNode = s + } + } return u.fastNode, nil }) if shared && touch { // a shared fastSingle.Do() may cause providers untouched, so we touch them again @@ -114,7 +144,6 @@ func (u *URLTest) SupportUDP() bool { if u.disableUDP { return false } - return u.fast(false).SupportUDP() } @@ -161,6 +190,7 @@ func NewURLTest(option *GroupCommonOption, providers []provider.ProxyProvider, o }), fastSingle: singledo.NewSingle[C.Proxy](time.Second * 10), disableUDP: option.DisableUDP, + testUrl: option.URL, } for _, option := range options { From e9c9d17a9b7b8d3d69338af72fd195caf94a9033 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 8 Apr 2023 09:56:02 +0800 Subject: [PATCH 25/88] chore: init gopsutil's Process direct from struct --- tunnel/statistic/manager.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/tunnel/statistic/manager.go b/tunnel/statistic/manager.go index 7d0ee5e4..66e3857d 100644 --- a/tunnel/statistic/manager.go +++ b/tunnel/statistic/manager.go @@ -19,7 +19,7 @@ func init() { downloadBlip: atomic.NewInt64(0), uploadTotal: atomic.NewInt64(0), downloadTotal: atomic.NewInt64(0), - pid: os.Getpid(), + process: &process.Process{Pid: int32(os.Getpid())}, } go DefaultManager.handle() @@ -33,7 +33,7 @@ type Manager struct { downloadBlip *atomic.Int64 uploadTotal *atomic.Int64 downloadTotal *atomic.Int64 - pid int + process *process.Process memory uint64 } @@ -71,11 +71,7 @@ func (m *Manager) Snapshot() *Snapshot { }) getMem := func() uint64 { - p, err := process.NewProcess(int32(m.pid)) - if err != nil { - return 0 - } - stat, err := p.MemoryInfo() + stat, err := m.process.MemoryInfo() if err != nil { return 0 } From 8e8cddf4624c4dfa022e1b9834d883350cbd07e5 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 9 Apr 2023 15:40:17 +0800 Subject: [PATCH 26/88] chore: Update dependencies --- common/utils/uuid.go | 2 +- common/utils/uuid_test.go | 2 +- constant/context.go | 2 +- context/conn.go | 2 +- context/dns.go | 2 +- context/packetconn.go | 2 +- go.mod | 18 +++++++++--------- go.sum | 35 ++++++++++++++++++----------------- transport/tuic/server.go | 2 +- transport/vless/conn.go | 2 +- transport/vless/vision.go | 2 +- transport/vless/vless.go | 2 +- transport/vmess/user.go | 2 +- transport/vmess/vmess.go | 2 +- tunnel/statistic/tracker.go | 2 +- 15 files changed, 40 insertions(+), 39 deletions(-) diff --git a/common/utils/uuid.go b/common/utils/uuid.go index 930fda7d..f559b471 100644 --- a/common/utils/uuid.go +++ b/common/utils/uuid.go @@ -1,7 +1,7 @@ package utils import ( - "github.com/gofrs/uuid" + "github.com/gofrs/uuid/v5" "github.com/zhangyunhao116/fastrand" ) diff --git a/common/utils/uuid_test.go b/common/utils/uuid_test.go index ba00ea08..3e0507d8 100644 --- a/common/utils/uuid_test.go +++ b/common/utils/uuid_test.go @@ -1,7 +1,7 @@ package utils import ( - "github.com/gofrs/uuid" + "github.com/gofrs/uuid/v5" "reflect" "testing" ) diff --git a/constant/context.go b/constant/context.go index da1e4155..1c70124b 100644 --- a/constant/context.go +++ b/constant/context.go @@ -5,7 +5,7 @@ import ( N "github.com/Dreamacro/clash/common/net" - "github.com/gofrs/uuid" + "github.com/gofrs/uuid/v5" ) type PlainContext interface { diff --git a/context/conn.go b/context/conn.go index c5477780..afeed852 100644 --- a/context/conn.go +++ b/context/conn.go @@ -7,7 +7,7 @@ import ( N "github.com/Dreamacro/clash/common/net" C "github.com/Dreamacro/clash/constant" - "github.com/gofrs/uuid" + "github.com/gofrs/uuid/v5" ) type ConnContext struct { diff --git a/context/dns.go b/context/dns.go index c41de724..ae29154f 100644 --- a/context/dns.go +++ b/context/dns.go @@ -4,7 +4,7 @@ import ( "context" "github.com/Dreamacro/clash/common/utils" - "github.com/gofrs/uuid" + "github.com/gofrs/uuid/v5" "github.com/miekg/dns" ) diff --git a/context/packetconn.go b/context/packetconn.go index b9afef41..d695bae5 100644 --- a/context/packetconn.go +++ b/context/packetconn.go @@ -6,7 +6,7 @@ import ( "github.com/Dreamacro/clash/common/utils" C "github.com/Dreamacro/clash/constant" - "github.com/gofrs/uuid" + "github.com/gofrs/uuid/v5" ) type PacketConnContext struct { diff --git a/go.mod b/go.mod index 68e3a09b..275e9595 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/go-chi/chi/v5 v5.0.8 github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.2 - github.com/gofrs/uuid v4.4.0+incompatible + github.com/gofrs/uuid/v5 v5.0.0 github.com/google/gopacket v1.1.19 github.com/gorilla/websocket v1.5.0 github.com/hashicorp/golang-lru v0.5.4 @@ -20,7 +20,7 @@ require ( github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 - github.com/metacubex/sing-shadowsocks v0.2.1 + github.com/metacubex/sing-shadowsocks v0.2.2-0.20230409073201-1ce3505114ae github.com/metacubex/sing-tun v0.1.3 github.com/metacubex/sing-wireguard v0.0.0-20230402083957-d134f603ac98 github.com/miekg/dns v1.1.52 @@ -28,9 +28,9 @@ require ( github.com/openacid/low v0.1.21 github.com/oschwald/geoip2-golang v1.8.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 - github.com/sagernet/sing v0.2.1 - github.com/sagernet/sing-shadowtls v0.1.0 - github.com/sagernet/sing-vmess v0.1.3 + github.com/sagernet/sing v0.2.2 + github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661 + github.com/sagernet/sing-vmess v0.1.4-0.20230409073451-6921c3dd77c7 github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c @@ -43,11 +43,11 @@ require ( go.etcd.io/bbolt v1.3.6 go.uber.org/atomic v1.10.0 go.uber.org/automaxprocs v1.5.2 - golang.org/x/crypto v0.7.0 + golang.org/x/crypto v0.8.0 golang.org/x/exp v0.0.0-20230321023759-10a507213a29 - golang.org/x/net v0.8.0 + golang.org/x/net v0.9.0 golang.org/x/sync v0.1.0 - golang.org/x/sys v0.6.0 + golang.org/x/sys v0.7.0 google.golang.org/protobuf v1.28.2-0.20230118093459-a9481185b34d gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.1.7 @@ -98,7 +98,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.2 // indirect gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect golang.org/x/mod v0.8.0 // indirect - golang.org/x/text v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect golang.org/x/tools v0.6.0 // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect diff --git a/go.sum b/go.sum index d6320fbe..05dc5abe 100644 --- a/go.sum +++ b/go.sum @@ -45,8 +45,8 @@ github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= -github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M= +github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= 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= @@ -96,8 +96,8 @@ github.com/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03 h1:gREIdurac9fpyB github.com/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03/go.mod h1:wqEuzdImyqD2MCGE8CYRJXbB77oSEJeoSSXXdwKjnsE= github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 h1:KD96JPdTIayTGGgRl6PuVqo2Bpo6+x3LqDDyqrYDDXw= github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594/go.mod h1:9nOiGX6kqV3+ZbkDKdTNzdFD726QQHPH6WDb36jUSpA= -github.com/metacubex/sing-shadowsocks v0.2.1 h1:tpSZQIzXT/qWDNPZjnb6jnj/htkcESLRJ37SNg2mXy4= -github.com/metacubex/sing-shadowsocks v0.2.1/go.mod h1:PNgGOvhangviWQkUH/spVwVt5w42nvs8jYckRh5WoCM= +github.com/metacubex/sing-shadowsocks v0.2.2-0.20230409073201-1ce3505114ae h1:SNnvqnJrqVErz1Qoo3thxa4lgdTPgBSQpJKQJcQjz9Y= +github.com/metacubex/sing-shadowsocks v0.2.2-0.20230409073201-1ce3505114ae/go.mod h1:JefDbbkQHCn8w+IcjFqMOMV2VmN9wvbYX4RaeBUkPwM= github.com/metacubex/sing-tun v0.1.3 h1:LCz8TlAZUWwnBYZxs1PEE04PyfLA9xdsye6CjHZlZGQ= github.com/metacubex/sing-tun v0.1.3/go.mod h1:kHkYHoRlYA4I12QPTt/ADyRGqy5YweLUfye1E1hC/q4= github.com/metacubex/sing-wireguard v0.0.0-20230402083957-d134f603ac98 h1:Dr6A4drhVkjPaJ5lIf3DwhH2HSo+Qmf8fCQmJoDG5YU= @@ -144,12 +144,12 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= -github.com/sagernet/sing v0.2.1 h1:r0STYeyfKBBtoAHsBtW1dQonxG+3Qidde7/1VAMhdn8= -github.com/sagernet/sing v0.2.1/go.mod h1:9uHswk2hITw8leDbiLS/xn0t9nzBcbePxzm9PJhwdlw= -github.com/sagernet/sing-shadowtls v0.1.0 h1:05MYce8aR5xfKIn+y7xRFsdKhKt44QZTSEQW+lG5IWQ= -github.com/sagernet/sing-shadowtls v0.1.0/go.mod h1:Kn1VUIprdkwCgkS6SXYaLmIpKzQbqBIKJBMY+RvBhYc= -github.com/sagernet/sing-vmess v0.1.3 h1:q/+tsF46dvvapL6CpQBgPHJ6nQrDUZqEtLHCbsjO7iM= -github.com/sagernet/sing-vmess v0.1.3/go.mod h1:GVXqAHwe9U21uS+Voh4YBIrADQyE4F9v0ayGSixSQAE= +github.com/sagernet/sing v0.2.2 h1:qfEdSLuwFgIIkeLOcwVQkVDzKLHtclXb93Ql0zZA+aE= +github.com/sagernet/sing v0.2.2/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= +github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661 h1:QnV79JbJbJGT0MJJfd8o7QMEfRu3eUVKsmahxFMonrc= +github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661/go.mod h1:xCeSRP8cV32aPsY+6BbRdJjyD6q8ufdKwhgqxEbU/3U= +github.com/sagernet/sing-vmess v0.1.4-0.20230409073451-6921c3dd77c7 h1:ZArINfN+zcHMdZCeRFOm4rO3SWyvYuLg3VhWcA5zonc= +github.com/sagernet/sing-vmess v0.1.4-0.20230409073451-6921c3dd77c7/go.mod h1:A9iGfwYmAEmVg8bavkqQ7Hx7nr9Pc2oWMYDwbLIBDjY= github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE= github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9/go.mod h1:FUyTEc5ye5NjKnDTDMuiLF2M6T4BE6y6KZuax//UCEg= github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfIATJ8oQwBmpOZJuozQG7Vk88lL4= @@ -206,8 +206,8 @@ go.uber.org/automaxprocs v1.5.2 h1:2LxUOGiR3O6tw8ui5sZa2LAaHnsviZdVOUZw4fvbnME= go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= +golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -218,8 +218,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= +golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= @@ -240,13 +240,14 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/transport/tuic/server.go b/transport/tuic/server.go index 88169aed..00c33fcb 100644 --- a/transport/tuic/server.go +++ b/transport/tuic/server.go @@ -17,7 +17,7 @@ import ( C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/transport/socks5" - "github.com/gofrs/uuid" + "github.com/gofrs/uuid/v5" "github.com/metacubex/quic-go" ) diff --git a/transport/vless/conn.go b/transport/vless/conn.go index 1f7d2cb3..6c3714e0 100644 --- a/transport/vless/conn.go +++ b/transport/vless/conn.go @@ -18,7 +18,7 @@ import ( tlsC "github.com/Dreamacro/clash/component/tls" "github.com/Dreamacro/clash/log" - "github.com/gofrs/uuid" + "github.com/gofrs/uuid/v5" utls "github.com/sagernet/utls" xtls "github.com/xtls/go" "google.golang.org/protobuf/proto" diff --git a/transport/vless/vision.go b/transport/vless/vision.go index 8dc84e40..5649dde4 100644 --- a/transport/vless/vision.go +++ b/transport/vless/vision.go @@ -7,7 +7,7 @@ import ( "github.com/Dreamacro/clash/common/buf" "github.com/Dreamacro/clash/log" - "github.com/gofrs/uuid" + "github.com/gofrs/uuid/v5" "github.com/zhangyunhao116/fastrand" ) diff --git a/transport/vless/vless.go b/transport/vless/vless.go index 6989374c..c2066afe 100644 --- a/transport/vless/vless.go +++ b/transport/vless/vless.go @@ -5,7 +5,7 @@ import ( "github.com/Dreamacro/clash/common/utils" - "github.com/gofrs/uuid" + "github.com/gofrs/uuid/v5" ) const ( diff --git a/transport/vmess/user.go b/transport/vmess/user.go index c098389e..091df0a8 100644 --- a/transport/vmess/user.go +++ b/transport/vmess/user.go @@ -4,7 +4,7 @@ import ( "bytes" "crypto/md5" - "github.com/gofrs/uuid" + "github.com/gofrs/uuid/v5" ) // ID cmdKey length diff --git a/transport/vmess/vmess.go b/transport/vmess/vmess.go index ee7ce121..2dd071eb 100644 --- a/transport/vmess/vmess.go +++ b/transport/vmess/vmess.go @@ -7,7 +7,7 @@ import ( "github.com/Dreamacro/clash/common/utils" - "github.com/gofrs/uuid" + "github.com/gofrs/uuid/v5" "github.com/zhangyunhao116/fastrand" ) diff --git a/tunnel/statistic/tracker.go b/tunnel/statistic/tracker.go index 11b9c0cd..b5f65338 100644 --- a/tunnel/statistic/tracker.go +++ b/tunnel/statistic/tracker.go @@ -8,7 +8,7 @@ import ( "github.com/Dreamacro/clash/common/utils" C "github.com/Dreamacro/clash/constant" - "github.com/gofrs/uuid" + "github.com/gofrs/uuid/v5" "go.uber.org/atomic" ) From 99dfa4c73a24742c820b57b8311f18d9173cf9ae Mon Sep 17 00:00:00 2001 From: rookisbusy Date: Sun, 9 Apr 2023 19:00:45 +0800 Subject: [PATCH 27/88] fix: tun warn timeout --- listener/sing_tun/dns.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/listener/sing_tun/dns.go b/listener/sing_tun/dns.go index f2daaf0c..e5ec82e2 100644 --- a/listener/sing_tun/dns.go +++ b/listener/sing_tun/dns.go @@ -6,6 +6,7 @@ import ( "io" "net" "net/netip" + "os" "sync" "time" @@ -117,7 +118,8 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network. dest, err := conn.ReadPacket(buff) if err != nil { buff.Release() - if E.IsClosed(err) { + // ignore simple error + if err == os.ErrDeadlineExceeded || E.IsClosed(err) { break } return err From 20b0af9a03832fa015ba20469990e9e974ef6026 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sun, 9 Apr 2023 19:15:32 +0800 Subject: [PATCH 28/88] chore: fix build --- adapter/outbound/shadowsocks.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 1c64b3ca..ae4037dc 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -187,7 +187,7 @@ func (ss *ShadowSocks) ListenPacketWithDialer(ctx context.Context, dialer C.Dial if err != nil { return nil, err } - pc = ss.method.DialPacketConn(&bufio.BindPacketConn{PacketConn: pc, Addr: addr}) + pc = ss.method.DialPacketConn(bufio.NewBindPacketConn(pc, addr)) return newPacketConn(pc, ss), nil } From 6c76312e5c6e836da256cbaf3da6a7297beaff83 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 9 Apr 2023 22:58:05 +0800 Subject: [PATCH 29/88] chore: Add read deadline implementation --- adapter/outbound/base.go | 4 ++-- common/net/sing.go | 15 +++++++++++++++ go.mod | 2 +- go.sum | 4 ++-- listener/shadowsocks/tcp.go | 3 ++- listener/shadowsocks/udp.go | 3 ++- 6 files changed, 24 insertions(+), 7 deletions(-) diff --git a/adapter/outbound/base.go b/adapter/outbound/base.go index 156bc114..f356ae2d 100644 --- a/adapter/outbound/base.go +++ b/adapter/outbound/base.go @@ -199,7 +199,7 @@ func (c *conn) Upstream() any { } func NewConn(c net.Conn, a C.ProxyAdapter) C.Conn { - return &conn{N.NewExtendedConn(c), []string{a.Name()}, parseRemoteDestination(a.Addr())} + return &conn{N.NewDeadlineConn(c), []string{a.Name()}, parseRemoteDestination(a.Addr())} } type packetConn struct { @@ -230,7 +230,7 @@ func (c *packetConn) LocalAddr() net.Addr { } func newPacketConn(pc net.PacketConn, a C.ProxyAdapter) C.PacketConn { - return &packetConn{pc, []string{a.Name()}, a.Name(), utils.NewUUIDV4().String(), parseRemoteDestination(a.Addr())} + return &packetConn{N.NewDeadlinePacketConn(pc), []string{a.Name()}, a.Name(), utils.NewUUIDV4().String(), parseRemoteDestination(a.Addr())} } func parseRemoteDestination(addr string) string { diff --git a/common/net/sing.go b/common/net/sing.go index 5c980738..4aad5523 100644 --- a/common/net/sing.go +++ b/common/net/sing.go @@ -6,6 +6,7 @@ import ( "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/bufio" + "github.com/sagernet/sing/common/bufio/deadline" "github.com/sagernet/sing/common/network" ) @@ -17,6 +18,20 @@ type ExtendedConn = network.ExtendedConn type ExtendedWriter = network.ExtendedWriter type ExtendedReader = network.ExtendedReader +func NewDeadlineConn(conn net.Conn) ExtendedConn { + if dc, ok := conn.(*deadline.Conn); ok { + return dc + } + return deadline.NewConn(conn) +} + +func NewDeadlinePacketConn(pc net.PacketConn) net.PacketConn { + if dpc, ok := pc.(*deadline.PacketConn); ok { + return dpc + } + return deadline.NewPacketConn(bufio.NewPacketConn(pc)) +} + func NeedHandshake(conn any) bool { if earlyConn, isEarlyConn := common.Cast[network.EarlyConn](conn); isEarlyConn && earlyConn.NeedHandshake() { return true diff --git a/go.mod b/go.mod index 275e9595..e7ceadf9 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/openacid/low v0.1.21 github.com/oschwald/geoip2-golang v1.8.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 - github.com/sagernet/sing v0.2.2 + github.com/sagernet/sing v0.2.3-0.20230409094616-7f8eaee1b6c8 github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661 github.com/sagernet/sing-vmess v0.1.4-0.20230409073451-6921c3dd77c7 github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 diff --git a/go.sum b/go.sum index 05dc5abe..38a2e16c 100644 --- a/go.sum +++ b/go.sum @@ -144,8 +144,8 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= -github.com/sagernet/sing v0.2.2 h1:qfEdSLuwFgIIkeLOcwVQkVDzKLHtclXb93Ql0zZA+aE= -github.com/sagernet/sing v0.2.2/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= +github.com/sagernet/sing v0.2.3-0.20230409094616-7f8eaee1b6c8 h1:BWQjek8tNzDzCeHh/8yvjfZ8Id0tl+6pJ+gcPI8tjl8= +github.com/sagernet/sing v0.2.3-0.20230409094616-7f8eaee1b6c8/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661 h1:QnV79JbJbJGT0MJJfd8o7QMEfRu3eUVKsmahxFMonrc= github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661/go.mod h1:xCeSRP8cV32aPsY+6BbRdJjyD6q8ufdKwhgqxEbU/3U= github.com/sagernet/sing-vmess v0.1.4-0.20230409073451-6921c3dd77c7 h1:ZArINfN+zcHMdZCeRFOm4rO3SWyvYuLg3VhWcA5zonc= diff --git a/listener/shadowsocks/tcp.go b/listener/shadowsocks/tcp.go index c0fd490f..4e25a7b9 100644 --- a/listener/shadowsocks/tcp.go +++ b/listener/shadowsocks/tcp.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/Dreamacro/clash/adapter/inbound" + N "github.com/Dreamacro/clash/common/net" C "github.com/Dreamacro/clash/constant" LC "github.com/Dreamacro/clash/listener/config" "github.com/Dreamacro/clash/transport/shadowsocks/core" @@ -99,7 +100,7 @@ func (l *Listener) AddrList() (addrList []net.Addr) { } func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) { - conn = l.pickCipher.StreamConn(conn) + conn = N.NewDeadlineConn(l.pickCipher.StreamConn(conn)) target, err := socks5.ReadAddr(conn, make([]byte, socks5.MaxAddrLen)) if err != nil { diff --git a/listener/shadowsocks/udp.go b/listener/shadowsocks/udp.go index 3f058406..d884c2b8 100644 --- a/listener/shadowsocks/udp.go +++ b/listener/shadowsocks/udp.go @@ -4,6 +4,7 @@ import ( "net" "github.com/Dreamacro/clash/adapter/inbound" + N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/pool" "github.com/Dreamacro/clash/common/sockopt" C "github.com/Dreamacro/clash/constant" @@ -29,7 +30,7 @@ func NewUDP(addr string, pickCipher core.Cipher, in chan<- C.PacketAdapter) (*UD } sl := &UDPListener{l, false} - conn := pickCipher.PacketConn(l) + conn := N.NewDeadlinePacketConn(pickCipher.PacketConn(l)) go func() { for { buf := pool.Get(pool.RelayBufferSize) From 1dbefc40c8a44744262c6d68e66c6c4620cf5c26 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 9 Apr 2023 23:06:56 +0800 Subject: [PATCH 30/88] chore: better error ignore --- listener/sing/sing.go | 10 +++++++++- listener/sing_tun/dns.go | 5 +---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/listener/sing/sing.go b/listener/sing/sing.go index 2a5a7d50..7421a4f1 100644 --- a/listener/sing/sing.go +++ b/listener/sing/sing.go @@ -103,7 +103,7 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network. dest, err := conn.ReadPacket(buff) if err != nil { buff.Release() - if E.IsClosed(err) { + if ShouldIgnorePacketError(err) { break } return err @@ -128,6 +128,14 @@ func (h *ListenerHandler) NewError(ctx context.Context, err error) { log.Warnln("%s listener get error: %+v", h.Type.String(), err) } +func ShouldIgnorePacketError(err error) bool { + // ignore simple error + if E.IsTimeout(err) || E.IsClosed(err) || E.IsCanceled(err) { + return true + } + return false +} + type packet struct { conn *network.PacketConn mutex *sync.Mutex diff --git a/listener/sing_tun/dns.go b/listener/sing_tun/dns.go index e5ec82e2..dc33be91 100644 --- a/listener/sing_tun/dns.go +++ b/listener/sing_tun/dns.go @@ -6,7 +6,6 @@ import ( "io" "net" "net/netip" - "os" "sync" "time" @@ -18,7 +17,6 @@ import ( D "github.com/miekg/dns" "github.com/sagernet/sing/common/buf" - E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" "github.com/sagernet/sing/common/network" ) @@ -118,8 +116,7 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network. dest, err := conn.ReadPacket(buff) if err != nil { buff.Release() - // ignore simple error - if err == os.ErrDeadlineExceeded || E.IsClosed(err) { + if sing.ShouldIgnorePacketError(err) { break } return err From 87b9e3d977c4ae3dc8368e3a81908fd995ca5ba4 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 10 Apr 2023 08:54:10 +0800 Subject: [PATCH 31/88] feat: wireguard add `dialer-proxy` config to support chain forwarding --- adapter/outbound/wireguard.go | 27 ++++++++-- adapter/outboundgroup/relay.go | 46 ++-------------- adapter/outboundgroup/util.go | 27 ---------- component/proxydialer/proxydialer.go | 78 ++++++++++++++++++++++++++++ docs/config.yaml | 2 + 5 files changed, 106 insertions(+), 74 deletions(-) create mode 100644 component/proxydialer/proxydialer.go diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index f136a20a..834e5fe2 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -15,6 +15,7 @@ import ( CN "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/component/dialer" + "github.com/Dreamacro/clash/component/proxydialer" "github.com/Dreamacro/clash/component/resolver" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" @@ -48,6 +49,7 @@ type WireGuardOption struct { MTU int `proxy:"mtu,omitempty"` UDP bool `proxy:"udp,omitempty"` PersistentKeepalive int `proxy:"persistent-keepalive,omitempty"` + DialerProxy string `proxy:"dialer-proxy,omitempty"` Peers []WireGuardPeerOption `proxy:"peers,omitempty"` } @@ -64,17 +66,34 @@ type WireGuardPeerOption struct { } type wgSingDialer struct { - dialer dialer.Dialer + dialer dialer.Dialer + proxyName string } var _ N.Dialer = (*wgSingDialer)(nil) func (d *wgSingDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { - return d.dialer.DialContext(ctx, network, destination.String()) + var cDialer C.Dialer = d.dialer + if len(d.proxyName) > 0 { + pd, err := proxydialer.NewByName(d.proxyName, d.dialer) + if err != nil { + return nil, err + } + cDialer = pd + } + return cDialer.DialContext(ctx, network, destination.String()) } func (d *wgSingDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { - return d.dialer.ListenPacket(ctx, "udp", "", destination.AddrPort()) + var cDialer C.Dialer = d.dialer + if len(d.proxyName) > 0 { + pd, err := proxydialer.NewByName(d.proxyName, d.dialer) + if err != nil { + return nil, err + } + cDialer = pd + } + return cDialer.ListenPacket(ctx, "udp", "", destination.AddrPort()) } type wgNetDialer struct { @@ -130,7 +149,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { rmark: option.RoutingMark, prefer: C.NewDNSPrefer(option.IPVersion), }, - dialer: &wgSingDialer{dialer: dialer.NewDialer()}, + dialer: &wgSingDialer{dialer: dialer.NewDialer(), proxyName: option.DialerProxy}, } runtime.SetFinalizer(outbound, closeWireGuard) diff --git a/adapter/outboundgroup/relay.go b/adapter/outboundgroup/relay.go index e17ae132..74c2f73b 100644 --- a/adapter/outboundgroup/relay.go +++ b/adapter/outboundgroup/relay.go @@ -3,13 +3,9 @@ package outboundgroup import ( "context" "encoding/json" - "net" - "net/netip" - "strings" - "github.com/Dreamacro/clash/adapter/outbound" - N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/component/dialer" + "github.com/Dreamacro/clash/component/proxydialer" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/constant/provider" ) @@ -18,36 +14,6 @@ type Relay struct { *GroupBase } -type proxyDialer struct { - proxy C.Proxy - dialer C.Dialer -} - -func (p proxyDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { - currentMeta, err := addrToMetadata(address) - if err != nil { - return nil, err - } - if strings.Contains(network, "udp") { // should not support this operation - currentMeta.NetWork = C.UDP - pc, err := p.proxy.ListenPacketWithDialer(ctx, p.dialer, currentMeta) - if err != nil { - return nil, err - } - return N.NewBindPacketConn(pc, currentMeta.UDPAddr()), nil - } - return p.proxy.DialContextWithDialer(ctx, p.dialer, currentMeta) -} - -func (p proxyDialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) { - currentMeta, err := addrToMetadata(rAddrPort.String()) - if err != nil { - return nil, err - } - currentMeta.NetWork = C.UDP - return p.proxy.ListenPacketWithDialer(ctx, p.dialer, currentMeta) -} - // DialContext implements C.ProxyAdapter func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { proxies, chainProxies := r.proxies(metadata, true) @@ -61,10 +27,7 @@ func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d var d C.Dialer d = dialer.NewDialer(r.Base.DialOptions(opts...)...) for _, proxy := range proxies[:len(proxies)-1] { - d = proxyDialer{ - proxy: proxy, - dialer: d, - } + d = proxydialer.New(proxy, d) } last := proxies[len(proxies)-1] conn, err := last.DialContextWithDialer(ctx, d, metadata) @@ -95,10 +58,7 @@ func (r *Relay) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o var d C.Dialer d = dialer.NewDialer(r.Base.DialOptions(opts...)...) for _, proxy := range proxies[:len(proxies)-1] { - d = proxyDialer{ - proxy: proxy, - dialer: d, - } + d = proxydialer.New(proxy, d) } last := proxies[len(proxies)-1] pc, err := last.ListenPacketWithDialer(ctx, d, metadata) diff --git a/adapter/outboundgroup/util.go b/adapter/outboundgroup/util.go index 578011f8..adcda1aa 100644 --- a/adapter/outboundgroup/util.go +++ b/adapter/outboundgroup/util.go @@ -1,37 +1,10 @@ package outboundgroup import ( - "fmt" "net" - "net/netip" "time" - - C "github.com/Dreamacro/clash/constant" ) -func addrToMetadata(rawAddress string) (addr *C.Metadata, err error) { - host, port, err := net.SplitHostPort(rawAddress) - if err != nil { - err = fmt.Errorf("addrToMetadata failed: %w", err) - return - } - - if ip, err := netip.ParseAddr(host); err != nil { - addr = &C.Metadata{ - Host: host, - DstPort: port, - } - } else { - addr = &C.Metadata{ - Host: "", - DstIP: ip.Unmap(), - DstPort: port, - } - } - - return -} - func tcpKeepAlive(c net.Conn) { if tcp, ok := c.(*net.TCPConn); ok { _ = tcp.SetKeepAlive(true) diff --git a/component/proxydialer/proxydialer.go b/component/proxydialer/proxydialer.go new file mode 100644 index 00000000..83428d59 --- /dev/null +++ b/component/proxydialer/proxydialer.go @@ -0,0 +1,78 @@ +package proxydialer + +import ( + "context" + "fmt" + "net" + "net/netip" + "strings" + + N "github.com/Dreamacro/clash/common/net" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/tunnel" +) + +type proxyDialer struct { + proxy C.Proxy + dialer C.Dialer +} + +func New(proxy C.Proxy, dialer C.Dialer) C.Dialer { + return proxyDialer{proxy: proxy, dialer: dialer} +} + +func NewByName(proxyName string, dialer C.Dialer) (C.Dialer, error) { + proxies := tunnel.Proxies() + if proxy, ok := proxies[proxyName]; ok { + return New(proxy, dialer), nil + } + return nil, fmt.Errorf("proxyName[%s] not found", proxyName) +} + +func (p proxyDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { + currentMeta, err := addrToMetadata(address) + if err != nil { + return nil, err + } + if strings.Contains(network, "udp") { // using in wireguard outbound + currentMeta.NetWork = C.UDP + pc, err := p.proxy.ListenPacketWithDialer(ctx, p.dialer, currentMeta) + if err != nil { + return nil, err + } + return N.NewBindPacketConn(pc, currentMeta.UDPAddr()), nil + } + return p.proxy.DialContextWithDialer(ctx, p.dialer, currentMeta) +} + +func (p proxyDialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) { + currentMeta, err := addrToMetadata(rAddrPort.String()) + if err != nil { + return nil, err + } + currentMeta.NetWork = C.UDP + return p.proxy.ListenPacketWithDialer(ctx, p.dialer, currentMeta) +} + +func addrToMetadata(rawAddress string) (addr *C.Metadata, err error) { + host, port, err := net.SplitHostPort(rawAddress) + if err != nil { + err = fmt.Errorf("addrToMetadata failed: %w", err) + return + } + + if ip, err := netip.ParseAddr(host); err != nil { + addr = &C.Metadata{ + Host: host, + DstPort: port, + } + } else { + addr = &C.Metadata{ + Host: "", + DstIP: ip.Unmap(), + DstPort: port, + } + } + + return +} diff --git a/docs/config.yaml b/docs/config.yaml index 66eff537..11891478 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -629,6 +629,8 @@ proxies: # socks5 reserved: "U4An" # 数组格式也是合法的 # reserved: [209,98,59] + # 一个出站代理的标识。当值不为空时,将使用指定的 proxy 发出连接 + # dialer-proxy: "ss1" # 如果peers不为空,该段落中的allowed_ips不可为空;前面段落的server,port,ip,ipv6,public-key,pre-shared-key均会被忽略,但private-key会被保留且只能在顶层指定 # peers: # - server: 162.159.192.1 From 2c48d2da3fdace9fc36f5bf9d9971cda47bfd9cf Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 10 Apr 2023 10:13:36 +0800 Subject: [PATCH 32/88] chore: clarify the wireguard logging --- adapter/outbound/wireguard.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 834e5fe2..2eb56210 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -285,14 +285,14 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { } outbound.device = device.NewDevice(outbound.tunDevice, outbound.bind, &device.Logger{ Verbosef: func(format string, args ...interface{}) { - log.SingLogger.Debug(fmt.Sprintf(strings.ToLower(format), args...)) + log.SingLogger.Debug(fmt.Sprintf("[WG](%s) %s", option.Name, fmt.Sprintf(format, args...))) }, Errorf: func(format string, args ...interface{}) { - log.SingLogger.Error(fmt.Sprintf(strings.ToLower(format), args...)) + log.SingLogger.Error(fmt.Sprintf("[WG](%s) %s", option.Name, fmt.Sprintf(format, args...))) }, }, option.Workers) if debug.Enabled { - log.SingLogger.Trace("created wireguard ipc conf: \n", ipcConf) + log.SingLogger.Trace(fmt.Sprintf("[WG](%s) created wireguard ipc conf: \n %s", option.Name, ipcConf)) } err = outbound.device.IpcSet(ipcConf) if err != nil { From 9afcb7071ff59c9992468f81d248587f6faa8b53 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 10 Apr 2023 11:20:28 +0800 Subject: [PATCH 33/88] feat: support `dialer-proxy` config for all outbound --- adapter/outbound/base.go | 1 + adapter/outbound/http.go | 7 ++++++ adapter/outbound/hysteria.go | 42 ++++++++++++++++++-------------- adapter/outbound/shadowsocks.go | 13 ++++++++++ adapter/outbound/shadowsocksr.go | 15 ++++++++++++ adapter/outbound/snell.go | 26 +++++++++++++++++++- adapter/outbound/socks5.go | 18 +++++++++++++- adapter/outbound/trojan.go | 23 ++++++++++++++++- adapter/outbound/tuic.go | 9 +++++++ adapter/outbound/vless.go | 23 ++++++++++++++++- adapter/outbound/vmess.go | 23 ++++++++++++++++- adapter/outbound/wireguard.go | 1 - 12 files changed, 177 insertions(+), 24 deletions(-) diff --git a/adapter/outbound/base.go b/adapter/outbound/base.go index f356ae2d..76f9bd39 100644 --- a/adapter/outbound/base.go +++ b/adapter/outbound/base.go @@ -146,6 +146,7 @@ type BasicOption struct { Interface string `proxy:"interface-name,omitempty" group:"interface-name,omitempty"` RoutingMark int `proxy:"routing-mark,omitempty" group:"routing-mark,omitempty"` IPVersion string `proxy:"ip-version,omitempty" group:"ip-version,omitempty"` + DialerProxy string `proxy:"dialer-proxy,omitempty"` // don't apply this option into groups, but can set a group name in a proxy } type BaseOption struct { diff --git a/adapter/outbound/http.go b/adapter/outbound/http.go index 6a668ebb..645177a4 100644 --- a/adapter/outbound/http.go +++ b/adapter/outbound/http.go @@ -14,6 +14,7 @@ import ( "strconv" "github.com/Dreamacro/clash/component/dialer" + "github.com/Dreamacro/clash/component/proxydialer" tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" ) @@ -66,6 +67,12 @@ func (h *Http) DialContext(ctx context.Context, metadata *C.Metadata, opts ...di // DialContextWithDialer implements C.ProxyAdapter func (h *Http) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { + if len(h.option.DialerProxy) > 0 { + dialer, err = proxydialer.NewByName(h.option.DialerProxy, dialer) + if err != nil { + return nil, err + } + } c, err := dialer.DialContext(ctx, "tcp", h.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %w", h.addr, err) diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index bd75cc3c..6024ea10 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -20,6 +20,7 @@ import ( M "github.com/sagernet/sing/common/metadata" "github.com/Dreamacro/clash/component/dialer" + "github.com/Dreamacro/clash/component/proxydialer" tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" @@ -28,6 +29,7 @@ import ( "github.com/Dreamacro/clash/transport/hysteria/obfs" "github.com/Dreamacro/clash/transport/hysteria/pmtud_fix" "github.com/Dreamacro/clash/transport/hysteria/transport" + "github.com/Dreamacro/clash/transport/hysteria/utils" ) const ( @@ -46,21 +48,12 @@ var rateStringRegexp = regexp.MustCompile(`^(\d+)\s*([KMGT]?)([Bb])ps$`) type Hysteria struct { *Base + option *HysteriaOption client *core.Client } func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { - hdc := hyDialerWithContext{ - ctx: context.Background(), - hyDialer: func(network string) (net.PacketConn, error) { - return dialer.ListenPacket(ctx, network, "", h.Base.DialOptions(opts...)...) - }, - remoteAddr: func(addr string) (net.Addr, error) { - return resolveUDPAddrWithPrefer(ctx, "udp", addr, h.prefer) - }, - } - - tcpConn, err := h.client.DialTCP(metadata.RemoteAddress(), &hdc) + tcpConn, err := h.client.DialTCP(metadata.RemoteAddress(), h.genHdc(ctx, opts...)) if err != nil { return nil, err } @@ -69,20 +62,32 @@ func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata, opts . } func (h *Hysteria) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { - hdc := hyDialerWithContext{ + udpConn, err := h.client.DialUDP(h.genHdc(ctx, opts...)) + if err != nil { + return nil, err + } + return newPacketConn(&hyPacketConn{udpConn}, h), nil +} + +func (h *Hysteria) genHdc(ctx context.Context, opts ...dialer.Option) utils.PacketDialer { + return &hyDialerWithContext{ ctx: context.Background(), hyDialer: func(network string) (net.PacketConn, error) { - return dialer.ListenPacket(ctx, network, "", h.Base.DialOptions(opts...)...) + var err error + var cDialer C.Dialer = dialer.NewDialer(h.Base.DialOptions(opts...)...) + if len(h.option.DialerProxy) > 0 { + cDialer, err = proxydialer.NewByName(h.option.DialerProxy, cDialer) + if err != nil { + return nil, err + } + } + rAddrPort, _ := netip.ParseAddrPort(h.Addr()) + return cDialer.ListenPacket(ctx, network, "", rAddrPort) }, remoteAddr: func(addr string) (net.Addr, error) { return resolveUDPAddrWithPrefer(ctx, "udp", addr, h.prefer) }, } - udpConn, err := h.client.DialUDP(&hdc) - if err != nil { - return nil, err - } - return newPacketConn(&hyPacketConn{udpConn}, h), nil } type HysteriaOption struct { @@ -258,6 +263,7 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { rmark: option.RoutingMark, prefer: C.NewDNSPrefer(option.IPVersion), }, + option: &option, client: client, }, nil } diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index ae4037dc..ac4cc31e 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -11,6 +11,7 @@ import ( N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/structure" "github.com/Dreamacro/clash/component/dialer" + "github.com/Dreamacro/clash/component/proxydialer" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/transport/restls" obfs "github.com/Dreamacro/clash/transport/simple-obfs" @@ -145,6 +146,12 @@ func (ss *ShadowSocks) DialContext(ctx context.Context, metadata *C.Metadata, op // DialContextWithDialer implements C.ProxyAdapter func (ss *ShadowSocks) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { + if len(ss.option.DialerProxy) > 0 { + dialer, err = proxydialer.NewByName(ss.option.DialerProxy, dialer) + if err != nil { + return nil, err + } + } c, err := dialer.DialContext(ctx, "tcp", ss.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %w", ss.addr, err) @@ -166,6 +173,12 @@ func (ss *ShadowSocks) ListenPacketContext(ctx context.Context, metadata *C.Meta // ListenPacketWithDialer implements C.ProxyAdapter func (ss *ShadowSocks) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) { + if len(ss.option.DialerProxy) > 0 { + dialer, err = proxydialer.NewByName(ss.option.DialerProxy, dialer) + if err != nil { + return nil, err + } + } if ss.option.UDPOverTCP { tcpConn, err := ss.DialContextWithDialer(ctx, dialer, metadata) if err != nil { diff --git a/adapter/outbound/shadowsocksr.go b/adapter/outbound/shadowsocksr.go index 135e7132..e96116d4 100644 --- a/adapter/outbound/shadowsocksr.go +++ b/adapter/outbound/shadowsocksr.go @@ -7,6 +7,7 @@ import ( "strconv" "github.com/Dreamacro/clash/component/dialer" + "github.com/Dreamacro/clash/component/proxydialer" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/transport/shadowsocks/core" "github.com/Dreamacro/clash/transport/shadowsocks/shadowaead" @@ -17,6 +18,7 @@ import ( type ShadowSocksR struct { *Base + option *ShadowSocksROption cipher core.Cipher obfs obfs.Obfs protocol protocol.Protocol @@ -65,6 +67,12 @@ func (ssr *ShadowSocksR) DialContext(ctx context.Context, metadata *C.Metadata, // DialContextWithDialer implements C.ProxyAdapter func (ssr *ShadowSocksR) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { + if len(ssr.option.DialerProxy) > 0 { + dialer, err = proxydialer.NewByName(ssr.option.DialerProxy, dialer) + if err != nil { + return nil, err + } + } c, err := dialer.DialContext(ctx, "tcp", ssr.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %w", ssr.addr, err) @@ -86,6 +94,12 @@ func (ssr *ShadowSocksR) ListenPacketContext(ctx context.Context, metadata *C.Me // ListenPacketWithDialer implements C.ProxyAdapter func (ssr *ShadowSocksR) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) { + if len(ssr.option.DialerProxy) > 0 { + dialer, err = proxydialer.NewByName(ssr.option.DialerProxy, dialer) + if err != nil { + return nil, err + } + } addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ssr.addr, ssr.prefer) if err != nil { return nil, err @@ -168,6 +182,7 @@ func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) { rmark: option.RoutingMark, prefer: C.NewDNSPrefer(option.IPVersion), }, + option: &option, cipher: coreCiph, obfs: obfs, protocol: protocol, diff --git a/adapter/outbound/snell.go b/adapter/outbound/snell.go index d6f1efee..d6d0436d 100644 --- a/adapter/outbound/snell.go +++ b/adapter/outbound/snell.go @@ -8,6 +8,7 @@ import ( "github.com/Dreamacro/clash/common/structure" "github.com/Dreamacro/clash/component/dialer" + "github.com/Dreamacro/clash/component/proxydialer" C "github.com/Dreamacro/clash/constant" obfs "github.com/Dreamacro/clash/transport/simple-obfs" "github.com/Dreamacro/clash/transport/snell" @@ -15,6 +16,7 @@ import ( type Snell struct { *Base + option *SnellOption psk []byte pool *snell.Pool obfsOption *simpleObfsOption @@ -83,6 +85,12 @@ func (s *Snell) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d // DialContextWithDialer implements C.ProxyAdapter func (s *Snell) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { + if len(s.option.DialerProxy) > 0 { + dialer, err = proxydialer.NewByName(s.option.DialerProxy, dialer) + if err != nil { + return nil, err + } + } c, err := dialer.DialContext(ctx, "tcp", s.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %w", s.addr, err) @@ -104,6 +112,13 @@ func (s *Snell) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o // ListenPacketWithDialer implements C.ProxyAdapter func (s *Snell) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (C.PacketConn, error) { + var err error + if len(s.option.DialerProxy) > 0 { + dialer, err = proxydialer.NewByName(s.option.DialerProxy, dialer) + if err != nil { + return nil, err + } + } c, err := dialer.DialContext(ctx, "tcp", s.addr) if err != nil { return nil, err @@ -172,6 +187,7 @@ func NewSnell(option SnellOption) (*Snell, error) { rmark: option.RoutingMark, prefer: C.NewDNSPrefer(option.IPVersion), }, + option: &option, psk: psk, obfsOption: obfsOption, version: option.Version, @@ -179,7 +195,15 @@ func NewSnell(option SnellOption) (*Snell, error) { if option.Version == snell.Version2 { s.pool = snell.NewPool(func(ctx context.Context) (*snell.Snell, error) { - c, err := dialer.DialContext(ctx, "tcp", addr, s.Base.DialOptions()...) + var err error + var cDialer C.Dialer = dialer.NewDialer(s.Base.DialOptions()...) + if len(s.option.DialerProxy) > 0 { + cDialer, err = proxydialer.NewByName(s.option.DialerProxy, cDialer) + if err != nil { + return nil, err + } + } + c, err := cDialer.DialContext(ctx, "tcp", addr) if err != nil { return nil, err } diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go index cdb89cc2..b328b634 100644 --- a/adapter/outbound/socks5.go +++ b/adapter/outbound/socks5.go @@ -10,6 +10,7 @@ import ( "strconv" "github.com/Dreamacro/clash/component/dialer" + "github.com/Dreamacro/clash/component/proxydialer" tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/transport/socks5" @@ -17,6 +18,7 @@ import ( type Socks5 struct { *Base + option *Socks5Option user string pass string tls bool @@ -70,6 +72,12 @@ func (ss *Socks5) DialContext(ctx context.Context, metadata *C.Metadata, opts .. // DialContextWithDialer implements C.ProxyAdapter func (ss *Socks5) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { + if len(ss.option.DialerProxy) > 0 { + dialer, err = proxydialer.NewByName(ss.option.DialerProxy, dialer) + if err != nil { + return nil, err + } + } c, err := dialer.DialContext(ctx, "tcp", ss.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %w", ss.addr, err) @@ -95,7 +103,14 @@ func (ss *Socks5) SupportWithDialer() bool { // ListenPacketContext implements C.ProxyAdapter func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { - c, err := dialer.DialContext(ctx, "tcp", ss.addr, ss.Base.DialOptions(opts...)...) + var cDialer C.Dialer = dialer.NewDialer(ss.Base.DialOptions(opts...)...) + if len(ss.option.DialerProxy) > 0 { + cDialer, err = proxydialer.NewByName(ss.option.DialerProxy, cDialer) + if err != nil { + return nil, err + } + } + c, err := cDialer.DialContext(ctx, "tcp", ss.addr) if err != nil { err = fmt.Errorf("%s connect error: %w", ss.addr, err) return @@ -187,6 +202,7 @@ func NewSocks5(option Socks5Option) (*Socks5, error) { rmark: option.RoutingMark, prefer: C.NewDNSPrefer(option.IPVersion), }, + option: &option, user: option.UserName, pass: option.Password, tls: option.TLS, diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index 4a31538b..013164c4 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -9,6 +9,7 @@ import ( "strconv" "github.com/Dreamacro/clash/component/dialer" + "github.com/Dreamacro/clash/component/proxydialer" tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/transport/gun" @@ -134,6 +135,12 @@ func (t *Trojan) DialContext(ctx context.Context, metadata *C.Metadata, opts ... // DialContextWithDialer implements C.ProxyAdapter func (t *Trojan) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { + if len(t.option.DialerProxy) > 0 { + dialer, err = proxydialer.NewByName(t.option.DialerProxy, dialer) + if err != nil { + return nil, err + } + } c, err := dialer.DialContext(ctx, "tcp", t.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %w", t.addr, err) @@ -178,6 +185,12 @@ func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata, // ListenPacketWithDialer implements C.ProxyAdapter func (t *Trojan) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) { + if len(t.option.DialerProxy) > 0 { + dialer, err = proxydialer.NewByName(t.option.DialerProxy, dialer) + if err != nil { + return nil, err + } + } c, err := dialer.DialContext(ctx, "tcp", t.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %w", t.addr, err) @@ -270,7 +283,15 @@ func NewTrojan(option TrojanOption) (*Trojan, error) { if option.Network == "grpc" { dialFn := func(network, addr string) (net.Conn, error) { - c, err := dialer.DialContext(context.Background(), "tcp", t.addr, t.Base.DialOptions()...) + var err error + var cDialer C.Dialer = dialer.NewDialer(t.Base.DialOptions()...) + if len(t.option.DialerProxy) > 0 { + cDialer, err = proxydialer.NewByName(t.option.DialerProxy, cDialer) + if err != nil { + return nil, err + } + } + c, err := cDialer.DialContext(context.Background(), "tcp", t.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %s", t.addr, err.Error()) } diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index d2f2b5e9..6640c46a 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -16,6 +16,7 @@ import ( "github.com/metacubex/quic-go" "github.com/Dreamacro/clash/component/dialer" + "github.com/Dreamacro/clash/component/proxydialer" tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/transport/tuic" @@ -23,6 +24,7 @@ import ( type Tuic struct { *Base + option *TuicOption client *tuic.PoolClient } @@ -93,6 +95,12 @@ func (t *Tuic) dial(ctx context.Context, opts ...dialer.Option) (pc net.PacketCo } func (t *Tuic) dialWithDialer(ctx context.Context, dialer C.Dialer) (pc net.PacketConn, addr net.Addr, err error) { + if len(t.option.DialerProxy) > 0 { + dialer, err = proxydialer.NewByName(t.option.DialerProxy, dialer) + if err != nil { + return nil, nil, err + } + } udpAddr, err := resolveUDPAddrWithPrefer(ctx, "udp", t.addr, t.prefer) if err != nil { return nil, nil, err @@ -230,6 +238,7 @@ func NewTuic(option TuicOption) (*Tuic, error) { rmark: option.RoutingMark, prefer: C.NewDNSPrefer(option.IPVersion), }, + option: &option, } clientMaxOpenStreams := int64(option.MaxOpenStreams) diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index 757661cc..b5ae66e1 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -14,6 +14,7 @@ import ( "github.com/Dreamacro/clash/common/convert" "github.com/Dreamacro/clash/component/dialer" + "github.com/Dreamacro/clash/component/proxydialer" "github.com/Dreamacro/clash/component/resolver" tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" @@ -238,6 +239,12 @@ func (v *Vless) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d // DialContextWithDialer implements C.ProxyAdapter func (v *Vless) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { + if len(v.option.DialerProxy) > 0 { + dialer, err = proxydialer.NewByName(v.option.DialerProxy, dialer) + if err != nil { + return nil, err + } + } c, err := dialer.DialContext(ctx, "tcp", v.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) @@ -297,6 +304,12 @@ func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o // ListenPacketWithDialer implements C.ProxyAdapter func (v *Vless) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) { + if len(v.option.DialerProxy) > 0 { + dialer, err = proxydialer.NewByName(v.option.DialerProxy, dialer) + if err != nil { + return nil, err + } + } // vless use stream-oriented udp with a special address, so we need a net.UDPAddr if !metadata.Resolved() { ip, err := resolver.ResolveIP(ctx, metadata.Host) @@ -538,7 +551,15 @@ func NewVless(option VlessOption) (*Vless, error) { } case "grpc": dialFn := func(network, addr string) (net.Conn, error) { - c, err := dialer.DialContext(context.Background(), "tcp", v.addr, v.Base.DialOptions()...) + var err error + var cDialer C.Dialer = dialer.NewDialer(v.Base.DialOptions()...) + if len(v.option.DialerProxy) > 0 { + cDialer, err = proxydialer.NewByName(v.option.DialerProxy, cDialer) + if err != nil { + return nil, err + } + } + c, err := cDialer.DialContext(context.Background(), "tcp", v.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index 5bb46dad..048b381c 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -13,6 +13,7 @@ import ( N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/component/dialer" + "github.com/Dreamacro/clash/component/proxydialer" "github.com/Dreamacro/clash/component/resolver" tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" @@ -263,6 +264,12 @@ func (v *Vmess) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d // DialContextWithDialer implements C.ProxyAdapter func (v *Vmess) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { + if len(v.option.DialerProxy) > 0 { + dialer, err = proxydialer.NewByName(v.option.DialerProxy, dialer) + if err != nil { + return nil, err + } + } c, err := dialer.DialContext(ctx, "tcp", v.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) @@ -329,6 +336,12 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o // ListenPacketWithDialer implements C.ProxyAdapter func (v *Vmess) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) { + if len(v.option.DialerProxy) > 0 { + dialer, err = proxydialer.NewByName(v.option.DialerProxy, dialer) + if err != nil { + return nil, err + } + } // vmess use stream-oriented udp with a special address, so we need a net.UDPAddr if !metadata.Resolved() { ip, err := resolver.ResolveIP(ctx, metadata.Host) @@ -428,7 +441,15 @@ func NewVmess(option VmessOption) (*Vmess, error) { } case "grpc": dialFn := func(network, addr string) (net.Conn, error) { - c, err := dialer.DialContext(context.Background(), "tcp", v.addr, v.Base.DialOptions()...) + var err error + var cDialer C.Dialer = dialer.NewDialer(v.Base.DialOptions()...) + if len(v.option.DialerProxy) > 0 { + cDialer, err = proxydialer.NewByName(v.option.DialerProxy, cDialer) + if err != nil { + return nil, err + } + } + c, err := cDialer.DialContext(context.Background(), "tcp", v.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 2eb56210..9a302de5 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -49,7 +49,6 @@ type WireGuardOption struct { MTU int `proxy:"mtu,omitempty"` UDP bool `proxy:"udp,omitempty"` PersistentKeepalive int `proxy:"persistent-keepalive,omitempty"` - DialerProxy string `proxy:"dialer-proxy,omitempty"` Peers []WireGuardPeerOption `proxy:"peers,omitempty"` } From 4d12ed491cd23c99a56d61961640b6f2c40b9794 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 10 Apr 2023 12:22:12 +0800 Subject: [PATCH 34/88] fix: tuic pool client should only cache the system's UDPConn --- transport/tuic/pool_client.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/transport/tuic/pool_client.go b/transport/tuic/pool_client.go index fe06c2f3..04ada7c0 100644 --- a/transport/tuic/pool_client.go +++ b/transport/tuic/pool_client.go @@ -67,11 +67,14 @@ func (t *PoolClient) dial(ctx context.Context, dialer C.Dialer, dialFn DialFunc) return nil, nil, err } - dr.pc, dr.addr, dr.err = pc, addr, err + if _, ok := pc.(*net.UDPConn); ok { // only cache the system's UDPConn + dr.pc, dr.addr, dr.err = pc, addr, err + + t.dialResultMutex.Lock() + t.dialResultMap[dialer] = dr + t.dialResultMutex.Unlock() + } - t.dialResultMutex.Lock() - t.dialResultMap[dialer] = dr - t.dialResultMutex.Unlock() return pc, addr, err } From 304b4d9bcba30800a282894447fab4bf51c47d85 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Mon, 10 Apr 2023 21:03:31 +0800 Subject: [PATCH 35/88] chore: download geoX use inner --- component/geodata/init.go | 18 +++++++++++++----- component/mmdb/mmdb.go | 10 ++++++++-- config/updateGeo.go | 17 ++++++++++++----- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/component/geodata/init.go b/component/geodata/init.go index f7dd7a9e..acae1a34 100644 --- a/component/geodata/init.go +++ b/component/geodata/init.go @@ -1,13 +1,17 @@ package geodata import ( + "context" "fmt" - "github.com/Dreamacro/clash/component/mmdb" - C "github.com/Dreamacro/clash/constant" - "github.com/Dreamacro/clash/log" "io" "net/http" "os" + "time" + + clashHttp "github.com/Dreamacro/clash/component/http" + "github.com/Dreamacro/clash/component/mmdb" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/log" ) var initGeoSite bool @@ -38,7 +42,9 @@ func InitGeoSite() error { } func downloadGeoSite(path string) (err error) { - resp, err := http.Get(C.GeoSiteUrl) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) + defer cancel() + resp, err := clashHttp.HttpRequest(ctx, C.GeoSiteUrl, http.MethodGet, http.Header{"User-Agent": {"clash"}}, nil) if err != nil { return } @@ -55,7 +61,9 @@ func downloadGeoSite(path string) (err error) { } func downloadGeoIP(path string) (err error) { - resp, err := http.Get(C.GeoIpUrl) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) + defer cancel() + resp, err := clashHttp.HttpRequest(ctx, C.GeoIpUrl, http.MethodGet, http.Header{"User-Agent": {"clash"}}, nil) if err != nil { return } diff --git a/component/mmdb/mmdb.go b/component/mmdb/mmdb.go index 8f28d486..14f6f997 100644 --- a/component/mmdb/mmdb.go +++ b/component/mmdb/mmdb.go @@ -1,14 +1,18 @@ package mmdb import ( - "github.com/oschwald/geoip2-golang" + "context" "io" "net/http" "os" "sync" + "time" + clashHttp "github.com/Dreamacro/clash/component/http" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" + + "github.com/oschwald/geoip2-golang" ) var ( @@ -47,7 +51,9 @@ func Instance() *geoip2.Reader { } func DownloadMMDB(path string) (err error) { - resp, err := http.Get(C.MmdbUrl) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) + defer cancel() + resp, err := clashHttp.HttpRequest(ctx, C.MmdbUrl, http.MethodGet, http.Header{"User-Agent": {"clash"}}, nil) if err != nil { return } diff --git a/config/updateGeo.go b/config/updateGeo.go index 698bd52d..e76301ba 100644 --- a/config/updateGeo.go +++ b/config/updateGeo.go @@ -1,15 +1,20 @@ package config import ( + "context" "fmt" - "github.com/Dreamacro/clash/component/geodata" - _ "github.com/Dreamacro/clash/component/geodata/standard" - C "github.com/Dreamacro/clash/constant" - "github.com/oschwald/geoip2-golang" "io" "net/http" "os" "runtime" + "time" + + "github.com/Dreamacro/clash/component/geodata" + _ "github.com/Dreamacro/clash/component/geodata/standard" + clashHttp "github.com/Dreamacro/clash/component/http" + C "github.com/Dreamacro/clash/constant" + + "github.com/oschwald/geoip2-golang" ) func UpdateGeoDatabases() error { @@ -69,7 +74,9 @@ func UpdateGeoDatabases() error { } func downloadForBytes(url string) ([]byte, error) { - resp, err := http.Get(url) + ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) + defer cancel() + resp, err := clashHttp.HttpRequest(ctx, url, http.MethodGet, http.Header{"User-Agent": {"clash"}}, nil) if err != nil { return nil, err } From ecdde647b176f702258abd7165747f771a0a1ed9 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Mon, 10 Apr 2023 21:13:23 +0800 Subject: [PATCH 36/88] chore: cleanup listener before restart --- hub/route/restart.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hub/route/restart.go b/hub/route/restart.go index 69b4f5b8..6c3f27f3 100644 --- a/hub/route/restart.go +++ b/hub/route/restart.go @@ -8,6 +8,7 @@ import ( "runtime" "syscall" + "github.com/Dreamacro/clash/listener" "github.com/Dreamacro/clash/log" "github.com/go-chi/chi/v5" @@ -43,7 +44,7 @@ func restart(w http.ResponseWriter, r *http.Request) { func runRestart(execPath string) { var err error - + listener.Cleanup(false) if runtime.GOOS == "windows" { cmd := exec.Command(execPath, os.Args[1:]...) log.Infoln("restarting: %q %q", execPath, os.Args[1:]) From ab3fce29ab88ec113ce7a75f0937bf983c49f696 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 11 Apr 2023 10:29:55 +0800 Subject: [PATCH 37/88] feat: wireguard add `remote-dns-resolve` and `dns` settings --- adapter/outbound/wireguard.go | 44 ++++++++++++++++++++++++++++++--- config/config.go | 24 +++++++++++------- dns/client.go | 11 +++++---- dns/doh.go | 28 ++++++++++++--------- dns/doq.go | 15 +++++++----- dns/resolver.go | 7 ++++-- dns/util.go | 46 ++++++++++++++++++++--------------- docs/config.yaml | 2 ++ 8 files changed, 121 insertions(+), 56 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 9a302de5..0070ce46 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -18,6 +18,7 @@ import ( "github.com/Dreamacro/clash/component/proxydialer" "github.com/Dreamacro/clash/component/resolver" C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/dns" "github.com/Dreamacro/clash/log" wireguard "github.com/metacubex/sing-wireguard" @@ -38,6 +39,7 @@ type WireGuard struct { dialer *wgSingDialer startOnce sync.Once startErr error + resolver *dns.Resolver } type WireGuardOption struct { @@ -51,6 +53,9 @@ type WireGuardOption struct { PersistentKeepalive int `proxy:"persistent-keepalive,omitempty"` Peers []WireGuardPeerOption `proxy:"peers,omitempty"` + + RemoteDnsResolve bool `proxy:"remote-dns-resolve,omitempty"` + Dns []string `proxy:"dns,omitempty"` } type WireGuardPeerOption struct { @@ -298,6 +303,29 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { return nil, E.Cause(err, "setup wireguard") } //err = outbound.tunDevice.Start() + + var has6 bool + for _, address := range localPrefixes { + if !address.Addr().Unmap().Is4() { + has6 = true + break + } + } + + if option.RemoteDnsResolve && len(option.Dns) > 0 { + nss, err := dns.ParseNameServer(option.Dns) + if err != nil { + return nil, err + } + for i := range nss { + nss[i].ProxyAdapter = outbound + } + outbound.resolver = dns.NewResolver(dns.Config{ + Main: nss, + IPv6: has6, + }) + } + return outbound, nil } @@ -318,8 +346,12 @@ func (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata, opts if w.startErr != nil { return nil, w.startErr } - if !metadata.Resolved() { - options = append(options, dialer.WithResolver(resolver.DefaultResolver)) + if !metadata.Resolved() || w.resolver != nil { + r := resolver.DefaultResolver + if w.resolver != nil { + r = w.resolver + } + options = append(options, dialer.WithResolver(r)) options = append(options, dialer.WithNetDialer(wgNetDialer{tunDevice: w.tunDevice})) conn, err = dialer.NewDialer(options...).DialContext(ctx, "tcp", metadata.RemoteAddress()) } else { @@ -348,8 +380,12 @@ func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadat if err != nil { return nil, err } - if !metadata.Resolved() { - ip, err := resolver.ResolveIP(ctx, metadata.Host) + if (!metadata.Resolved() || w.resolver != nil) && metadata.Host != "" { + r := resolver.DefaultResolver + if w.resolver != nil { + r = w.resolver + } + ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) if err != nil { return nil, errors.New("can't resolve ip") } diff --git a/config/config.go b/config/config.go index e7720c40..24594ee2 100644 --- a/config/config.go +++ b/config/config.go @@ -896,7 +896,7 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error) return nil, fmt.Errorf("DNS NameServer[%d] format error: %s", idx, err.Error()) } - proxyAdapter := u.Fragment + proxyName := u.Fragment var addr, dnsNetType string params := map[string]string{} @@ -913,7 +913,7 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error) case "https": addr, err = hostWithDefaultPort(u.Host, "443") if err == nil { - proxyAdapter = "" + proxyName = "" clearURL := url.URL{Scheme: "https", Host: addr, Path: u.Path} addr = clearURL.String() dnsNetType = "https" // DNS over HTTPS @@ -923,7 +923,7 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error) if len(arr) == 0 { continue } else if len(arr) == 1 { - proxyAdapter = arr[0] + proxyName = arr[0] } else if len(arr) == 2 { params[arr[0]] = arr[1] } else { @@ -949,18 +949,24 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error) nameservers = append( nameservers, dns.NameServer{ - Net: dnsNetType, - Addr: addr, - ProxyAdapter: proxyAdapter, - Interface: dialer.DefaultInterface, - Params: params, - PreferH3: preferH3, + Net: dnsNetType, + Addr: addr, + ProxyName: proxyName, + Interface: dialer.DefaultInterface, + Params: params, + PreferH3: preferH3, }, ) } return nameservers, nil } +func init() { + dns.ParseNameServer = func(servers []string) ([]dns.NameServer, error) { // using by wireguard + return parseNameServer(servers, false) + } +} + func parsePureDNSServer(server string) string { addPre := func(server string) string { return "udp://" + server diff --git a/dns/client.go b/dns/client.go index 936a5882..637207f3 100644 --- a/dns/client.go +++ b/dns/client.go @@ -8,14 +8,14 @@ import ( "net/netip" "strings" - tlsC "github.com/Dreamacro/clash/component/tls" - "go.uber.org/atomic" - "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/resolver" + tlsC "github.com/Dreamacro/clash/component/tls" + C "github.com/Dreamacro/clash/constant" D "github.com/miekg/dns" "github.com/zhangyunhao116/fastrand" + "go.uber.org/atomic" ) type client struct { @@ -24,7 +24,8 @@ type client struct { port string host string iface *atomic.String - proxyAdapter string + proxyAdapter C.ProxyAdapter + proxyName string addr string } @@ -81,7 +82,7 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) options = append(options, dialer.WithInterface(c.iface.Load())) } - conn, err := getDialHandler(c.r, c.proxyAdapter, options...)(ctx, network, net.JoinHostPort(ip.String(), c.port)) + conn, err := getDialHandler(c.r, c.proxyAdapter, c.proxyName, options...)(ctx, network, net.JoinHostPort(ip.String(), c.port)) if err != nil { return nil, err } diff --git a/dns/doh.go b/dns/doh.go index 1e6528d9..dd8ba435 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -21,6 +21,7 @@ import ( "github.com/metacubex/quic-go" "github.com/metacubex/quic-go/http3" D "github.com/miekg/dns" + "golang.org/x/exp/slices" "golang.org/x/net/http2" ) @@ -63,7 +64,8 @@ type dnsOverHTTPS struct { url *url.URL r *Resolver httpVersions []C.HTTPVersion - proxyAdapter string + proxyAdapter C.ProxyAdapter + proxyName string addr string } @@ -71,7 +73,7 @@ type dnsOverHTTPS struct { var _ dnsClient = (*dnsOverHTTPS)(nil) // newDoH returns the DNS-over-HTTPS Upstream. -func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[string]string, proxyAdapter string) dnsClient { +func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[string]string, proxyAdapter C.ProxyAdapter, proxyName string) dnsClient { u, _ := url.Parse(urlString) httpVersions := DefaultHTTPVersions if preferH3 { @@ -87,6 +89,7 @@ func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[strin addr: u.String(), r: r, proxyAdapter: proxyAdapter, + proxyName: proxyName, quicConfig: &quic.Config{ KeepAlivePeriod: QUICKeepAlivePeriod, TokenStore: newQUICTokenStore(), @@ -390,14 +393,17 @@ func (doh *dnsOverHTTPS) createTransport(ctx context.Context) (t http.RoundTripp nextProtos = append(nextProtos, string(v)) } tlsConfig.NextProtos = nextProtos - dialContext := getDialHandler(doh.r, doh.proxyAdapter) - // First, we attempt to create an HTTP3 transport. If the probe QUIC - // connection is established successfully, we'll be using HTTP3 for this - // upstream. - transportH3, err := doh.createTransportH3(ctx, tlsConfig, dialContext) - if err == nil { - log.Debugln("[%s] using HTTP/3 for this upstream: QUIC was faster", doh.url.String()) - return transportH3, nil + dialContext := getDialHandler(doh.r, doh.proxyAdapter, doh.proxyName) + + if slices.Contains(doh.httpVersions, C.HTTPVersion3) { + // First, we attempt to create an HTTP3 transport. If the probe QUIC + // connection is established successfully, we'll be using HTTP3 for this + // upstream. + transportH3, err := doh.createTransportH3(ctx, tlsConfig, dialContext) + if err == nil { + log.Debugln("[%s] using HTTP/3 for this upstream: QUIC was faster", doh.url.String()) + return transportH3, nil + } } log.Debugln("[%s] using HTTP/2 for this upstream: %v", doh.url.String(), err) @@ -533,7 +539,7 @@ func (doh *dnsOverHTTPS) dialQuic(ctx context.Context, addr string, tlsCfg *tls. IP: net.ParseIP(ip), Port: portInt, } - conn, err := listenPacket(ctx, doh.proxyAdapter, "udp", addr, doh.r) + conn, err := listenPacket(ctx, doh.proxyAdapter, doh.proxyName, "udp", addr, doh.r) if err != nil { return nil, err } diff --git a/dns/doq.go b/dns/doq.go index 1354f177..73310340 100644 --- a/dns/doq.go +++ b/dns/doq.go @@ -13,9 +13,10 @@ import ( "time" tlsC "github.com/Dreamacro/clash/component/tls" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/log" "github.com/metacubex/quic-go" - "github.com/Dreamacro/clash/log" D "github.com/miekg/dns" ) @@ -60,7 +61,8 @@ type dnsOverQUIC struct { bytesPoolGuard sync.Mutex addr string - proxyAdapter string + proxyAdapter C.ProxyAdapter + proxyName string r *Resolver } @@ -68,10 +70,11 @@ type dnsOverQUIC struct { var _ dnsClient = (*dnsOverQUIC)(nil) // newDoQ returns the DNS-over-QUIC Upstream. -func newDoQ(resolver *Resolver, addr string, adapter string) (dnsClient, error) { +func newDoQ(resolver *Resolver, addr string, proxyAdapter C.ProxyAdapter, proxyName string) (dnsClient, error) { doq := &dnsOverQUIC{ addr: addr, - proxyAdapter: adapter, + proxyAdapter: proxyAdapter, + proxyName: proxyName, r: resolver, quicConfig: &quic.Config{ KeepAlivePeriod: QUICKeepAlivePeriod, @@ -310,7 +313,7 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connectio // we're using bootstrapped address instead of what's passed to the function // it does not create an actual connection, but it helps us determine // what IP is actually reachable (when there're v4/v6 addresses). - rawConn, err := getDialHandler(doq.r, doq.proxyAdapter)(ctx, "udp", doq.addr) + rawConn, err := getDialHandler(doq.r, doq.proxyAdapter, doq.proxyName)(ctx, "udp", doq.addr) if err != nil { return nil, fmt.Errorf("failed to open a QUIC connection: %w", err) } @@ -325,7 +328,7 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connectio p, err := strconv.Atoi(port) udpAddr := net.UDPAddr{IP: net.ParseIP(ip), Port: p} - udp, err := listenPacket(ctx, doq.proxyAdapter, "udp", addr, doq.r) + udp, err := listenPacket(ctx, doq.proxyAdapter, doq.proxyName, "udp", addr, doq.r) if err != nil { return nil, err } diff --git a/dns/resolver.go b/dns/resolver.go index b5a09fd0..25bfedf0 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -100,7 +100,7 @@ func (r *Resolver) LookupIP(ctx context.Context, host string) (ips []netip.Addr, ips, err = r.lookupIP(ctx, host, D.TypeA) var waitIPv6 *time.Timer - if r != nil { + if r != nil && r.ipv6Timeout > 0 { waitIPv6 = time.NewTimer(r.ipv6Timeout) } else { waitIPv6 = time.NewTimer(100 * time.Millisecond) @@ -421,7 +421,8 @@ type NameServer struct { Net string Addr string Interface *atomic.String - ProxyAdapter string + ProxyAdapter C.ProxyAdapter + ProxyName string Params map[string]string PreferH3 bool } @@ -544,3 +545,5 @@ func NewProxyServerHostResolver(old *Resolver) *Resolver { } return r } + +var ParseNameServer func(servers []string) ([]NameServer, error) // define in config/config.go diff --git a/dns/util.go b/dns/util.go index 4821195d..2fe85931 100644 --- a/dns/util.go +++ b/dns/util.go @@ -74,13 +74,13 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient { for _, s := range servers { switch s.Net { case "https": - ret = append(ret, newDoHClient(s.Addr, resolver, s.PreferH3, s.Params, s.ProxyAdapter)) + ret = append(ret, newDoHClient(s.Addr, resolver, s.PreferH3, s.Params, s.ProxyAdapter, s.ProxyName)) continue case "dhcp": ret = append(ret, newDHCPClient(s.Addr)) continue case "quic": - if doq, err := newDoQ(resolver, s.Addr, s.ProxyAdapter); err == nil { + if doq, err := newDoQ(resolver, s.Addr, s.ProxyAdapter, s.ProxyName); err == nil { ret = append(ret, doq) } else { log.Fatalln("DoQ format error: %v", err) @@ -103,6 +103,7 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient { iface: s.Interface, r: resolver, proxyAdapter: s.ProxyAdapter, + proxyName: s.ProxyName, }) } return ret @@ -144,9 +145,9 @@ func msgToDomain(msg *D.Msg) string { type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error) -func getDialHandler(r *Resolver, proxyAdapter string, opts ...dialer.Option) dialHandler { +func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) dialHandler { return func(ctx context.Context, network, addr string) (net.Conn, error) { - if len(proxyAdapter) == 0 { + if len(proxyName) == 0 && proxyAdapter == nil { opts = append(opts, dialer.WithResolver(r)) return dialer.DialContext(ctx, network, addr, opts...) } else { @@ -154,10 +155,14 @@ func getDialHandler(r *Resolver, proxyAdapter string, opts ...dialer.Option) dia if err != nil { return nil, err } - adapter, ok := tunnel.Proxies()[proxyAdapter] - if !ok { - opts = append(opts, dialer.WithInterface(proxyAdapter)) + if proxyAdapter == nil { + var ok bool + proxyAdapter, ok = tunnel.Proxies()[proxyName] + if !ok { + opts = append(opts, dialer.WithInterface(proxyName)) + } } + if strings.Contains(network, "tcp") { // tcp can resolve host by remote metadata := &C.Metadata{ @@ -165,8 +170,8 @@ func getDialHandler(r *Resolver, proxyAdapter string, opts ...dialer.Option) dia Host: host, DstPort: port, } - if ok { - return adapter.DialContext(ctx, metadata, opts...) + if proxyAdapter != nil { + return proxyAdapter.DialContext(ctx, metadata, opts...) } opts = append(opts, dialer.WithResolver(r)) return dialer.DialContext(ctx, network, addr, opts...) @@ -182,15 +187,15 @@ func getDialHandler(r *Resolver, proxyAdapter string, opts ...dialer.Option) dia DstIP: dstIP, DstPort: port, } - if !ok { + if proxyAdapter == nil { return dialer.DialContext(ctx, network, addr, opts...) } - if !adapter.SupportUDP() { + if !proxyAdapter.SupportUDP() { return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter) } - packetConn, err := adapter.ListenPacketContext(ctx, metadata, opts...) + packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...) if err != nil { return nil, err } @@ -201,14 +206,17 @@ func getDialHandler(r *Resolver, proxyAdapter string, opts ...dialer.Option) dia } } -func listenPacket(ctx context.Context, proxyAdapter string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.PacketConn, error) { +func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.PacketConn, error) { host, port, err := net.SplitHostPort(addr) if err != nil { return nil, err } - adapter, ok := tunnel.Proxies()[proxyAdapter] - if !ok && len(proxyAdapter) != 0 { - opts = append(opts, dialer.WithInterface(proxyAdapter)) + if proxyAdapter == nil { + var ok bool + proxyAdapter, ok = tunnel.Proxies()[proxyName] + if !ok { + opts = append(opts, dialer.WithInterface(proxyName)) + } } // udp must resolve host first @@ -222,15 +230,15 @@ func listenPacket(ctx context.Context, proxyAdapter string, network string, addr DstIP: dstIP, DstPort: port, } - if !ok { + if proxyAdapter == nil { return dialer.ListenPacket(ctx, dialer.ParseNetwork(network, dstIP), "", opts...) } - if !adapter.SupportUDP() { + if !proxyAdapter.SupportUDP() { return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter) } - return adapter.ListenPacketContext(ctx, metadata, opts...) + return proxyAdapter.ListenPacketContext(ctx, metadata, opts...) } func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) { diff --git a/docs/config.yaml b/docs/config.yaml index 11891478..99f40ce7 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -631,6 +631,8 @@ proxies: # socks5 # reserved: [209,98,59] # 一个出站代理的标识。当值不为空时,将使用指定的 proxy 发出连接 # dialer-proxy: "ss1" + # remote-dns-resolve: true # 强制dns远程解析,默认值为false + # dns: [ 1.1.1.1, 8.8.8.8 ] # 仅在remote-dns-resolve为true时生效 # 如果peers不为空,该段落中的allowed_ips不可为空;前面段落的server,port,ip,ipv6,public-key,pre-shared-key均会被忽略,但private-key会被保留且只能在顶层指定 # peers: # - server: 162.159.192.1 From 92cc268209e29c5a14252d843580193dd308b220 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 11 Apr 2023 12:51:24 +0800 Subject: [PATCH 38/88] chore: proxyDialer can limited support old dial function --- adapter/outbound/base.go | 15 ++++++------ adapter/outbound/http.go | 4 ++-- adapter/outbound/shadowsocks.go | 6 ++--- adapter/outbound/shadowsocksr.go | 4 ++-- adapter/outbound/snell.go | 4 ++-- adapter/outbound/socks5.go | 4 ++-- adapter/outbound/trojan.go | 4 ++-- adapter/outbound/tuic.go | 4 ++-- adapter/outbound/vless.go | 4 ++-- adapter/outbound/vmess.go | 4 ++-- adapter/outboundgroup/relay.go | 5 +++- component/dialer/dialer.go | 8 +++---- component/proxydialer/proxydialer.go | 36 +++++++++++++++++++++++----- constant/adapters.go | 5 +++- constant/metadata.go | 13 +++++++--- 15 files changed, 78 insertions(+), 42 deletions(-) diff --git a/adapter/outbound/base.go b/adapter/outbound/base.go index 76f9bd39..5b6ef705 100644 --- a/adapter/outbound/base.go +++ b/adapter/outbound/base.go @@ -3,7 +3,6 @@ package outbound import ( "context" "encoding/json" - "errors" "net" "strings" @@ -47,31 +46,31 @@ func (b *Base) Type() C.AdapterType { // StreamConn implements C.ProxyAdapter func (b *Base) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { - return c, errors.New("no support") + return c, C.ErrNotSupport } func (b *Base) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { - return nil, errors.New("no support") + return nil, C.ErrNotSupport } // DialContextWithDialer implements C.ProxyAdapter func (b *Base) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { - return nil, errors.New("no support") + return nil, C.ErrNotSupport } // ListenPacketContext implements C.ProxyAdapter func (b *Base) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { - return nil, errors.New("no support") + return nil, C.ErrNotSupport } // ListenPacketWithDialer implements C.ProxyAdapter func (b *Base) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) { - return nil, errors.New("no support") + return nil, C.ErrNotSupport } // SupportWithDialer implements C.ProxyAdapter -func (b *Base) SupportWithDialer() bool { - return false +func (b *Base) SupportWithDialer() C.NetWork { + return C.InvalidNet } // SupportUOT implements C.ProxyAdapter diff --git a/adapter/outbound/http.go b/adapter/outbound/http.go index 645177a4..2d5fd899 100644 --- a/adapter/outbound/http.go +++ b/adapter/outbound/http.go @@ -92,8 +92,8 @@ func (h *Http) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metad } // SupportWithDialer implements C.ProxyAdapter -func (h *Http) SupportWithDialer() bool { - return true +func (h *Http) SupportWithDialer() C.NetWork { + return C.TCP } func (h *Http) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error { diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index ac4cc31e..14bd96b0 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -205,8 +205,8 @@ func (ss *ShadowSocks) ListenPacketWithDialer(ctx context.Context, dialer C.Dial } // SupportWithDialer implements C.ProxyAdapter -func (ss *ShadowSocks) SupportWithDialer() bool { - return true +func (ss *ShadowSocks) SupportWithDialer() C.NetWork { + return C.ALLNet } // ListenPacketOnStreamConn implements C.ProxyAdapter @@ -219,7 +219,7 @@ func (ss *ShadowSocks) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata return newPacketConn(uot.NewLazyConn(c, uot.Request{Destination: destination}), ss), nil } } - return nil, errors.New("no support") + return nil, C.ErrNotSupport } // SupportUOT implements C.ProxyAdapter diff --git a/adapter/outbound/shadowsocksr.go b/adapter/outbound/shadowsocksr.go index e96116d4..2b94ab0c 100644 --- a/adapter/outbound/shadowsocksr.go +++ b/adapter/outbound/shadowsocksr.go @@ -116,8 +116,8 @@ func (ssr *ShadowSocksR) ListenPacketWithDialer(ctx context.Context, dialer C.Di } // SupportWithDialer implements C.ProxyAdapter -func (ssr *ShadowSocksR) SupportWithDialer() bool { - return true +func (ssr *ShadowSocksR) SupportWithDialer() C.NetWork { + return C.ALLNet } func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) { diff --git a/adapter/outbound/snell.go b/adapter/outbound/snell.go index d6d0436d..1ec0a430 100644 --- a/adapter/outbound/snell.go +++ b/adapter/outbound/snell.go @@ -136,8 +136,8 @@ func (s *Snell) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met } // SupportWithDialer implements C.ProxyAdapter -func (s *Snell) SupportWithDialer() bool { - return true +func (s *Snell) SupportWithDialer() C.NetWork { + return C.ALLNet } // SupportUOT implements C.ProxyAdapter diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go index b328b634..26f5733b 100644 --- a/adapter/outbound/socks5.go +++ b/adapter/outbound/socks5.go @@ -97,8 +97,8 @@ func (ss *Socks5) DialContextWithDialer(ctx context.Context, dialer C.Dialer, me } // SupportWithDialer implements C.ProxyAdapter -func (ss *Socks5) SupportWithDialer() bool { - return true +func (ss *Socks5) SupportWithDialer() C.NetWork { + return C.TCP } // ListenPacketContext implements C.ProxyAdapter diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index 013164c4..b9bbc33f 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -214,8 +214,8 @@ func (t *Trojan) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, me } // SupportWithDialer implements C.ProxyAdapter -func (t *Trojan) SupportWithDialer() bool { - return true +func (t *Trojan) SupportWithDialer() C.NetWork { + return C.ALLNet } // ListenPacketOnStreamConn implements C.ProxyAdapter diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index 6640c46a..f2452ae2 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -86,8 +86,8 @@ func (t *Tuic) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, meta } // SupportWithDialer implements C.ProxyAdapter -func (t *Tuic) SupportWithDialer() bool { - return true +func (t *Tuic) SupportWithDialer() C.NetWork { + return C.ALLNet } func (t *Tuic) dial(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) { diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index b5ae66e1..b7a22245 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -345,8 +345,8 @@ func (v *Vless) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met } // SupportWithDialer implements C.ProxyAdapter -func (v *Vless) SupportWithDialer() bool { - return true +func (v *Vless) SupportWithDialer() C.NetWork { + return C.ALLNet } // ListenPacketOnStreamConn implements C.ProxyAdapter diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index 048b381c..c5212eef 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -368,8 +368,8 @@ func (v *Vmess) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met } // SupportWithDialer implements C.ProxyAdapter -func (v *Vmess) SupportWithDialer() bool { - return true +func (v *Vmess) SupportWithDialer() C.NetWork { + return C.ALLNet } // ListenPacketOnStreamConn implements C.ProxyAdapter diff --git a/adapter/outboundgroup/relay.go b/adapter/outboundgroup/relay.go index 74c2f73b..c3fe8bd6 100644 --- a/adapter/outboundgroup/relay.go +++ b/adapter/outboundgroup/relay.go @@ -89,7 +89,10 @@ func (r *Relay) SupportUDP() bool { if proxy.SupportUOT() { return true } - if !proxy.SupportWithDialer() { + switch proxy.SupportWithDialer() { + case C.ALLNet: + case C.UDP: + default: // C.TCP and C.NONet return false } } diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index d70e9173..f6dfaf86 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -321,15 +321,15 @@ func sortationAddr(ips []netip.Addr) (ipv4s, ipv6s []netip.Addr) { } type Dialer struct { - opt option + Opt option } func (d Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { - return DialContext(ctx, network, address, WithOption(d.opt)) + return DialContext(ctx, network, address, WithOption(d.Opt)) } func (d Dialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) { - opt := WithOption(d.opt) + opt := WithOption(d.Opt) if rAddrPort.Addr().Unmap().IsLoopback() { // avoid "The requested address is not valid in its context." opt = WithInterface("") @@ -339,5 +339,5 @@ func (d Dialer) ListenPacket(ctx context.Context, network, address string, rAddr func NewDialer(options ...Option) Dialer { opt := applyOptions(options...) - return Dialer{opt: *opt} + return Dialer{Opt: *opt} } diff --git a/component/proxydialer/proxydialer.go b/component/proxydialer/proxydialer.go index 83428d59..fad3835d 100644 --- a/component/proxydialer/proxydialer.go +++ b/component/proxydialer/proxydialer.go @@ -8,16 +8,17 @@ import ( "strings" N "github.com/Dreamacro/clash/common/net" + "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/tunnel" ) type proxyDialer struct { - proxy C.Proxy + proxy C.ProxyAdapter dialer C.Dialer } -func New(proxy C.Proxy, dialer C.Dialer) C.Dialer { +func New(proxy C.ProxyAdapter, dialer C.Dialer) C.Dialer { return proxyDialer{proxy: proxy, dialer: dialer} } @@ -35,14 +36,23 @@ func (p proxyDialer) DialContext(ctx context.Context, network, address string) ( return nil, err } if strings.Contains(network, "udp") { // using in wireguard outbound - currentMeta.NetWork = C.UDP - pc, err := p.proxy.ListenPacketWithDialer(ctx, p.dialer, currentMeta) + pc, err := p.listenPacket(ctx, currentMeta) if err != nil { return nil, err } return N.NewBindPacketConn(pc, currentMeta.UDPAddr()), nil } - return p.proxy.DialContextWithDialer(ctx, p.dialer, currentMeta) + switch p.proxy.SupportWithDialer() { + case C.ALLNet: + fallthrough + case C.TCP: + return p.proxy.DialContextWithDialer(ctx, p.dialer, currentMeta) + default: // fallback to old function + if d, ok := p.dialer.(dialer.Dialer); ok { // fallback to old function + return p.proxy.DialContext(ctx, currentMeta, dialer.WithOption(d.Opt)) + } + return nil, C.ErrNotSupport + } } func (p proxyDialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) { @@ -50,8 +60,22 @@ func (p proxyDialer) ListenPacket(ctx context.Context, network, address string, if err != nil { return nil, err } + return p.listenPacket(ctx, currentMeta) +} + +func (p proxyDialer) listenPacket(ctx context.Context, currentMeta *C.Metadata) (net.PacketConn, error) { currentMeta.NetWork = C.UDP - return p.proxy.ListenPacketWithDialer(ctx, p.dialer, currentMeta) + switch p.proxy.SupportWithDialer() { + case C.ALLNet: + fallthrough + case C.UDP: + return p.proxy.ListenPacketWithDialer(ctx, p.dialer, currentMeta) + default: // fallback to old function + if d, ok := p.dialer.(dialer.Dialer); ok { // fallback to old function + return p.proxy.ListenPacketContext(ctx, currentMeta, dialer.WithOption(d.Opt)) + } + return nil, C.ErrNotSupport + } } func addrToMetadata(rawAddress string) (addr *C.Metadata, err error) { diff --git a/constant/adapters.go b/constant/adapters.go index ce9f9911..3b401734 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -2,6 +2,7 @@ package constant import ( "context" + "errors" "fmt" "net" "net/netip" @@ -44,6 +45,8 @@ const ( DefaultTLSTimeout = DefaultTCPTimeout ) +var ErrNotSupport = errors.New("no support") + type Connection interface { Chains() Chain AppendToChains(adapter ProxyAdapter) @@ -117,7 +120,7 @@ type ProxyAdapter interface { // SupportUOT return UDP over TCP support SupportUOT() bool - SupportWithDialer() bool + SupportWithDialer() NetWork DialContextWithDialer(ctx context.Context, dialer Dialer, metadata *Metadata) (Conn, error) ListenPacketWithDialer(ctx context.Context, dialer Dialer, metadata *Metadata) (PacketConn, error) diff --git a/constant/metadata.go b/constant/metadata.go index 599a6055..4ff20305 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -15,7 +15,10 @@ const ( TCP NetWork = iota UDP ALLNet + InvalidNet = 0xff +) +const ( HTTP Type = iota HTTPS SOCKS4 @@ -33,12 +36,16 @@ const ( type NetWork int func (n NetWork) String() string { - if n == TCP { + switch n { + case TCP: return "tcp" - } else if n == UDP { + case UDP: return "udp" + case ALLNet: + return "all" + default: + return "invalid" } - return "all" } func (n NetWork) MarshalJSON() ([]byte, error) { From 90f95d7c78b4d622b33b3277723e6166cf3f9eec Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 11 Apr 2023 14:10:57 +0800 Subject: [PATCH 39/88] chore: wireguard dns can work with domain-based server --- adapter/outbound/base.go | 5 +++++ adapter/outbound/wireguard.go | 5 +++++ adapter/outboundgroup/fallback.go | 5 +++++ adapter/outboundgroup/loadbalance.go | 5 +++++ adapter/outboundgroup/selector.go | 5 +++++ adapter/outboundgroup/urltest.go | 5 +++++ component/resolver/resolver.go | 7 ++++--- constant/adapters.go | 3 +++ dns/resolver.go | 7 +++++-- dns/util.go | 8 ++++++++ hub/executor/executor.go | 2 +- 11 files changed, 51 insertions(+), 6 deletions(-) diff --git a/adapter/outbound/base.go b/adapter/outbound/base.go index 5b6ef705..0d6cdb8c 100644 --- a/adapter/outbound/base.go +++ b/adapter/outbound/base.go @@ -93,6 +93,11 @@ func (b *Base) SupportTFO() bool { return b.tfo } +// IsL3Protocol implements C.ProxyAdapter +func (b *Base) IsL3Protocol(metadata *C.Metadata) bool { + return false +} + // MarshalJSON implements C.ProxyAdapter func (b *Base) MarshalJSON() ([]byte, error) { return json.Marshal(map[string]string{ diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 0070ce46..a2634be4 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -401,3 +401,8 @@ func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadat } return newPacketConn(CN.NewRefPacketConn(pc, w), w), nil } + +// IsL3Protocol implements C.ProxyAdapter +func (w *WireGuard) IsL3Protocol(metadata *C.Metadata) bool { + return true +} diff --git a/adapter/outboundgroup/fallback.go b/adapter/outboundgroup/fallback.go index 02ba0ac6..c79d9871 100644 --- a/adapter/outboundgroup/fallback.go +++ b/adapter/outboundgroup/fallback.go @@ -70,6 +70,11 @@ func (f *Fallback) SupportUDP() bool { return proxy.SupportUDP() } +// IsL3Protocol implements C.ProxyAdapter +func (f *Fallback) IsL3Protocol(metadata *C.Metadata) bool { + return f.findAliveProxy(false).IsL3Protocol(metadata) +} + // MarshalJSON implements C.ProxyAdapter func (f *Fallback) MarshalJSON() ([]byte, error) { all := []string{} diff --git a/adapter/outboundgroup/loadbalance.go b/adapter/outboundgroup/loadbalance.go index 15e17a13..607d4f4f 100644 --- a/adapter/outboundgroup/loadbalance.go +++ b/adapter/outboundgroup/loadbalance.go @@ -124,6 +124,11 @@ func (lb *LoadBalance) SupportUDP() bool { return !lb.disableUDP } +// IsL3Protocol implements C.ProxyAdapter +func (lb *LoadBalance) IsL3Protocol(metadata *C.Metadata) bool { + return lb.Unwrap(metadata, false).IsL3Protocol(metadata) +} + func strategyRoundRobin() strategyFn { idx := 0 idxMutex := sync.Mutex{} diff --git a/adapter/outboundgroup/selector.go b/adapter/outboundgroup/selector.go index 6356d10e..b5b1cfa3 100644 --- a/adapter/outboundgroup/selector.go +++ b/adapter/outboundgroup/selector.go @@ -44,6 +44,11 @@ func (s *Selector) SupportUDP() bool { return s.selectedProxy(false).SupportUDP() } +// IsL3Protocol implements C.ProxyAdapter +func (s *Selector) IsL3Protocol(metadata *C.Metadata) bool { + return s.selectedProxy(false).IsL3Protocol(metadata) +} + // MarshalJSON implements C.ProxyAdapter func (s *Selector) MarshalJSON() ([]byte, error) { all := []string{} diff --git a/adapter/outboundgroup/urltest.go b/adapter/outboundgroup/urltest.go index f3426a3e..6f10f78b 100644 --- a/adapter/outboundgroup/urltest.go +++ b/adapter/outboundgroup/urltest.go @@ -147,6 +147,11 @@ func (u *URLTest) SupportUDP() bool { return u.fast(false).SupportUDP() } +// IsL3Protocol implements C.ProxyAdapter +func (u *URLTest) IsL3Protocol(metadata *C.Metadata) bool { + return u.fast(false).IsL3Protocol(metadata) +} + // MarshalJSON implements C.ProxyAdapter func (u *URLTest) MarshalJSON() ([]byte, error) { all := []string{} diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index f5872ad7..3b5426d8 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -48,6 +48,7 @@ type Resolver interface { ResolveIPv4(ctx context.Context, host string) (ip netip.Addr, err error) ResolveIPv6(ctx context.Context, host string) (ip netip.Addr, err error) ExchangeContext(ctx context.Context, m *dns.Msg) (msg *dns.Msg, err error) + Invalid() bool } // LookupIPv4WithResolver same as LookupIPv4, but with a resolver @@ -68,7 +69,7 @@ func LookupIPv4WithResolver(ctx context.Context, host string, r Resolver) ([]net return []netip.Addr{}, ErrIPVersion } - if r != nil { + if r != nil && r.Invalid() { return r.LookupIPv4(ctx, host) } @@ -124,7 +125,7 @@ func LookupIPv6WithResolver(ctx context.Context, host string, r Resolver) ([]net return nil, ErrIPVersion } - if r != nil { + if r != nil && r.Invalid() { return r.LookupIPv6(ctx, host) } @@ -164,7 +165,7 @@ func LookupIPWithResolver(ctx context.Context, host string, r Resolver) ([]netip return node.IPs, nil } - if r != nil { + if r != nil && r.Invalid() { if DisableIPv6 { return r.LookupIPv4(ctx, host) } diff --git a/constant/adapters.go b/constant/adapters.go index 3b401734..2a2c68c1 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -124,6 +124,9 @@ type ProxyAdapter interface { DialContextWithDialer(ctx context.Context, dialer Dialer, metadata *Metadata) (Conn, error) ListenPacketWithDialer(ctx context.Context, dialer Dialer, metadata *Metadata) (PacketConn, error) + // IsL3Protocol return ProxyAdapter working in L3 (tell dns module not pass the domain to avoid loopback) + IsL3Protocol(metadata *Metadata) bool + // Unwrap extracts the proxy from a proxy-group. It returns nil when nothing to extract. Unwrap(metadata *Metadata, touch bool) Proxy } diff --git a/dns/resolver.go b/dns/resolver.go index 25bfedf0..467e9f14 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -412,8 +412,11 @@ func (r *Resolver) asyncExchange(ctx context.Context, client []dnsClient, msg *D return ch } -// HasProxyServer has proxy server dns client -func (r *Resolver) HasProxyServer() bool { +// Invalid return this resolver can or can't be used +func (r *Resolver) Invalid() bool { + if r == nil { + return false + } return len(r.main) > 0 } diff --git a/dns/util.go b/dns/util.go index 2fe85931..9df4482b 100644 --- a/dns/util.go +++ b/dns/util.go @@ -170,6 +170,14 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, Host: host, DstPort: port, } + if proxyAdapter.IsL3Protocol(metadata) { + dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r) + if err != nil { + return nil, err + } + metadata.Host = "" + metadata.DstIP = dstIP + } if proxyAdapter != nil { return proxyAdapter.DialContext(ctx, metadata, opts...) } diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 8ca844d2..3598d873 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -239,7 +239,7 @@ func updateDNS(c *config.DNS, ruleProvider map[string]provider.RuleProvider, gen resolver.DefaultHostMapper = m resolver.DefaultLocalServer = dns.NewLocalServer(r, m) - if pr.HasProxyServer() { + if pr.Invalid() { resolver.ProxyServerHostResolver = pr } From 7beb09153eb1b213cc9a5b3f3d8762e66c0c5245 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 11 Apr 2023 21:42:16 +0800 Subject: [PATCH 40/88] chore: proxyDialer can add inner conn to statistic --- adapter/outboundgroup/relay.go | 4 +-- component/proxydialer/proxydialer.go | 52 +++++++++++++++++++++------- 2 files changed, 42 insertions(+), 14 deletions(-) diff --git a/adapter/outboundgroup/relay.go b/adapter/outboundgroup/relay.go index c3fe8bd6..a596454f 100644 --- a/adapter/outboundgroup/relay.go +++ b/adapter/outboundgroup/relay.go @@ -27,7 +27,7 @@ func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d var d C.Dialer d = dialer.NewDialer(r.Base.DialOptions(opts...)...) for _, proxy := range proxies[:len(proxies)-1] { - d = proxydialer.New(proxy, d) + d = proxydialer.New(proxy, d, false) } last := proxies[len(proxies)-1] conn, err := last.DialContextWithDialer(ctx, d, metadata) @@ -58,7 +58,7 @@ func (r *Relay) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o var d C.Dialer d = dialer.NewDialer(r.Base.DialOptions(opts...)...) for _, proxy := range proxies[:len(proxies)-1] { - d = proxydialer.New(proxy, d) + d = proxydialer.New(proxy, d, false) } last := proxies[len(proxies)-1] pc, err := last.ListenPacketWithDialer(ctx, d, metadata) diff --git a/component/proxydialer/proxydialer.go b/component/proxydialer/proxydialer.go index fad3835d..6c1b3cf2 100644 --- a/component/proxydialer/proxydialer.go +++ b/component/proxydialer/proxydialer.go @@ -11,21 +11,23 @@ import ( "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/tunnel" + "github.com/Dreamacro/clash/tunnel/statistic" ) type proxyDialer struct { - proxy C.ProxyAdapter - dialer C.Dialer + proxy C.ProxyAdapter + dialer C.Dialer + statistic bool } -func New(proxy C.ProxyAdapter, dialer C.Dialer) C.Dialer { - return proxyDialer{proxy: proxy, dialer: dialer} +func New(proxy C.ProxyAdapter, dialer C.Dialer, statistic bool) C.Dialer { + return proxyDialer{proxy: proxy, dialer: dialer, statistic: statistic} } func NewByName(proxyName string, dialer C.Dialer) (C.Dialer, error) { proxies := tunnel.Proxies() if proxy, ok := proxies[proxyName]; ok { - return New(proxy, dialer), nil + return New(proxy, dialer, true), nil } return nil, fmt.Errorf("proxyName[%s] not found", proxyName) } @@ -42,17 +44,29 @@ func (p proxyDialer) DialContext(ctx context.Context, network, address string) ( } return N.NewBindPacketConn(pc, currentMeta.UDPAddr()), nil } + var conn C.Conn switch p.proxy.SupportWithDialer() { case C.ALLNet: fallthrough case C.TCP: - return p.proxy.DialContextWithDialer(ctx, p.dialer, currentMeta) + conn, err = p.proxy.DialContextWithDialer(ctx, p.dialer, currentMeta) + if err != nil { + return nil, err + } default: // fallback to old function if d, ok := p.dialer.(dialer.Dialer); ok { // fallback to old function - return p.proxy.DialContext(ctx, currentMeta, dialer.WithOption(d.Opt)) + conn, err = p.proxy.DialContext(ctx, currentMeta, dialer.WithOption(d.Opt)) + if err != nil { + return nil, err + } + } else { + return nil, C.ErrNotSupport } - return nil, C.ErrNotSupport } + if p.statistic { + conn = statistic.NewTCPTracker(conn, statistic.DefaultManager, currentMeta, nil, 0, 0) + } + return conn, err } func (p proxyDialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) { @@ -63,19 +77,32 @@ func (p proxyDialer) ListenPacket(ctx context.Context, network, address string, return p.listenPacket(ctx, currentMeta) } -func (p proxyDialer) listenPacket(ctx context.Context, currentMeta *C.Metadata) (net.PacketConn, error) { +func (p proxyDialer) listenPacket(ctx context.Context, currentMeta *C.Metadata) (C.PacketConn, error) { + var pc C.PacketConn + var err error currentMeta.NetWork = C.UDP switch p.proxy.SupportWithDialer() { case C.ALLNet: fallthrough case C.UDP: - return p.proxy.ListenPacketWithDialer(ctx, p.dialer, currentMeta) + pc, err = p.proxy.ListenPacketWithDialer(ctx, p.dialer, currentMeta) + if err != nil { + return nil, err + } default: // fallback to old function if d, ok := p.dialer.(dialer.Dialer); ok { // fallback to old function - return p.proxy.ListenPacketContext(ctx, currentMeta, dialer.WithOption(d.Opt)) + pc, err = p.proxy.ListenPacketContext(ctx, currentMeta, dialer.WithOption(d.Opt)) + if err != nil { + return nil, err + } + } else { + return nil, C.ErrNotSupport } - return nil, C.ErrNotSupport } + if p.statistic { + pc = statistic.NewUDPTracker(pc, statistic.DefaultManager, currentMeta, nil, 0, 0) + } + return pc, nil } func addrToMetadata(rawAddress string) (addr *C.Metadata, err error) { @@ -97,6 +124,7 @@ func addrToMetadata(rawAddress string) (addr *C.Metadata, err error) { DstPort: port, } } + addr.Type = C.INNER return } From bad7340a4e94214f4e932493fe79e6c97d6c7146 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 11 Apr 2023 23:58:56 +0800 Subject: [PATCH 41/88] chore: proxyDialer don't push flow to manager in statistic --- component/proxydialer/proxydialer.go | 4 +-- tunnel/statistic/tracker.go | 52 +++++++++++++++++++++++----- tunnel/tunnel.go | 4 +-- 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/component/proxydialer/proxydialer.go b/component/proxydialer/proxydialer.go index 6c1b3cf2..7fac628f 100644 --- a/component/proxydialer/proxydialer.go +++ b/component/proxydialer/proxydialer.go @@ -64,7 +64,7 @@ func (p proxyDialer) DialContext(ctx context.Context, network, address string) ( } } if p.statistic { - conn = statistic.NewTCPTracker(conn, statistic.DefaultManager, currentMeta, nil, 0, 0) + conn = statistic.NewTCPTracker(conn, statistic.DefaultManager, currentMeta, nil, 0, 0, false) } return conn, err } @@ -100,7 +100,7 @@ func (p proxyDialer) listenPacket(ctx context.Context, currentMeta *C.Metadata) } } if p.statistic { - pc = statistic.NewUDPTracker(pc, statistic.DefaultManager, currentMeta, nil, 0, 0) + pc = statistic.NewUDPTracker(pc, statistic.DefaultManager, currentMeta, nil, 0, 0, false) } return pc, nil } diff --git a/tunnel/statistic/tracker.go b/tunnel/statistic/tracker.go index b5f65338..3678a19d 100644 --- a/tunnel/statistic/tracker.go +++ b/tunnel/statistic/tracker.go @@ -32,6 +32,8 @@ type tcpTracker struct { C.Conn `json:"-"` *trackerInfo manager *Manager + + pushToManager bool `json:"-"` } func (tt *tcpTracker) ID() string { @@ -41,7 +43,9 @@ func (tt *tcpTracker) ID() string { func (tt *tcpTracker) Read(b []byte) (int, error) { n, err := tt.Conn.Read(b) download := int64(n) - tt.manager.PushDownloaded(download) + if tt.pushToManager { + tt.manager.PushDownloaded(download) + } tt.DownloadTotal.Add(download) return n, err } @@ -49,7 +53,9 @@ func (tt *tcpTracker) Read(b []byte) (int, error) { func (tt *tcpTracker) ReadBuffer(buffer *buf.Buffer) (err error) { err = tt.Conn.ReadBuffer(buffer) download := int64(buffer.Len()) - tt.manager.PushDownloaded(download) + if tt.pushToManager { + tt.manager.PushDownloaded(download) + } tt.DownloadTotal.Add(download) return } @@ -57,7 +63,9 @@ func (tt *tcpTracker) ReadBuffer(buffer *buf.Buffer) (err error) { func (tt *tcpTracker) Write(b []byte) (int, error) { n, err := tt.Conn.Write(b) upload := int64(n) - tt.manager.PushUploaded(upload) + if tt.pushToManager { + tt.manager.PushUploaded(upload) + } tt.UploadTotal.Add(upload) return n, err } @@ -65,7 +73,9 @@ func (tt *tcpTracker) Write(b []byte) (int, error) { func (tt *tcpTracker) WriteBuffer(buffer *buf.Buffer) (err error) { upload := int64(buffer.Len()) err = tt.Conn.WriteBuffer(buffer) - tt.manager.PushUploaded(upload) + if tt.pushToManager { + tt.manager.PushUploaded(upload) + } tt.UploadTotal.Add(upload) return } @@ -79,7 +89,7 @@ func (tt *tcpTracker) Upstream() any { return tt.Conn } -func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.Rule, uploadTotal int64, downloadTotal int64) *tcpTracker { +func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.Rule, uploadTotal int64, downloadTotal int64, pushToManager bool) *tcpTracker { if conn != nil { if tcpAddr, ok := conn.RemoteAddr().(*net.TCPAddr); ok { metadata.RemoteDst = tcpAddr.IP.String() @@ -100,6 +110,16 @@ func NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.R UploadTotal: atomic.NewInt64(uploadTotal), DownloadTotal: atomic.NewInt64(downloadTotal), }, + pushToManager: pushToManager, + } + + if pushToManager { + if uploadTotal > 0 { + manager.PushUploaded(uploadTotal) + } + if downloadTotal > 0 { + manager.PushDownloaded(downloadTotal) + } } if rule != nil { @@ -115,6 +135,8 @@ type udpTracker struct { C.PacketConn `json:"-"` *trackerInfo manager *Manager + + pushToManager bool `json:"-"` } func (ut *udpTracker) ID() string { @@ -124,7 +146,9 @@ func (ut *udpTracker) ID() string { func (ut *udpTracker) ReadFrom(b []byte) (int, net.Addr, error) { n, addr, err := ut.PacketConn.ReadFrom(b) download := int64(n) - ut.manager.PushDownloaded(download) + if ut.pushToManager { + ut.manager.PushDownloaded(download) + } ut.DownloadTotal.Add(download) return n, addr, err } @@ -132,7 +156,9 @@ func (ut *udpTracker) ReadFrom(b []byte) (int, net.Addr, error) { func (ut *udpTracker) WriteTo(b []byte, addr net.Addr) (int, error) { n, err := ut.PacketConn.WriteTo(b, addr) upload := int64(n) - ut.manager.PushUploaded(upload) + if ut.pushToManager { + ut.manager.PushUploaded(upload) + } ut.UploadTotal.Add(upload) return n, err } @@ -142,7 +168,7 @@ func (ut *udpTracker) Close() error { return ut.PacketConn.Close() } -func NewUDPTracker(conn C.PacketConn, manager *Manager, metadata *C.Metadata, rule C.Rule, uploadTotal int64, downloadTotal int64) *udpTracker { +func NewUDPTracker(conn C.PacketConn, manager *Manager, metadata *C.Metadata, rule C.Rule, uploadTotal int64, downloadTotal int64, pushToManager bool) *udpTracker { metadata.RemoteDst = conn.RemoteDestination() ut := &udpTracker{ @@ -157,6 +183,16 @@ func NewUDPTracker(conn C.PacketConn, manager *Manager, metadata *C.Metadata, ru UploadTotal: atomic.NewInt64(uploadTotal), DownloadTotal: atomic.NewInt64(downloadTotal), }, + pushToManager: pushToManager, + } + + if pushToManager { + if uploadTotal > 0 { + manager.PushUploaded(uploadTotal) + } + if downloadTotal > 0 { + manager.PushDownloaded(downloadTotal) + } } if rule != nil { diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index e982afa6..a4a473e9 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -364,7 +364,7 @@ func handleUDPConn(packet C.PacketAdapter) { } pCtx.InjectPacketConn(rawPc) - pc := statistic.NewUDPTracker(rawPc, statistic.DefaultManager, metadata, rule, 0, 0) + pc := statistic.NewUDPTracker(rawPc, statistic.DefaultManager, metadata, rule, 0, 0, true) switch true { case metadata.SpecialProxy != "": @@ -494,7 +494,7 @@ func handleTCPConn(connCtx C.ConnContext) { return } - remoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule, 0, int64(peekLen)) + remoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule, 0, int64(peekLen), true) defer func(remoteConn C.Conn) { _ = remoteConn.Close() }(remoteConn) From fda8857ec8158e1721d7af1089340051572e5f72 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 12 Apr 2023 10:39:24 +0800 Subject: [PATCH 42/88] feat: proxy-provider can set `dialer-proxy` too it will apply `dialer-proxy` to all proxy in this provider --- adapter/provider/parser.go | 4 +++- adapter/provider/provider.go | 9 ++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/adapter/provider/parser.go b/adapter/provider/parser.go index fc5ed936..1df7f320 100644 --- a/adapter/provider/parser.go +++ b/adapter/provider/parser.go @@ -28,6 +28,7 @@ type proxyProviderSchema struct { Filter string `provider:"filter,omitempty"` ExcludeFilter string `provider:"exclude-filter,omitempty"` ExcludeType string `provider:"exclude-type,omitempty"` + DialerProxy string `provider:"dialer-proxy,omitempty"` HealthCheck healthCheckSchema `provider:"health-check,omitempty"` } @@ -65,6 +66,7 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide filter := schema.Filter excludeFilter := schema.ExcludeFilter excludeType := schema.ExcludeType + dialerProxy := schema.DialerProxy - return NewProxySetProvider(name, interval, filter, excludeFilter, excludeType, vehicle, hc) + return NewProxySetProvider(name, interval, filter, excludeFilter, excludeType, dialerProxy, vehicle, hc) } diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index 4a2cf7b8..96852c4b 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -143,7 +143,7 @@ func stopProxyProvider(pd *ProxySetProvider) { _ = pd.Fetcher.Destroy() } -func NewProxySetProvider(name string, interval time.Duration, filter string, excludeFilter string, excludeType string, vehicle types.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) { +func NewProxySetProvider(name string, interval time.Duration, filter string, excludeFilter string, excludeType string, dialerProxy string, vehicle types.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) { excludeFilterReg, err := regexp2.Compile(excludeFilter, 0) if err != nil { return nil, fmt.Errorf("invalid excludeFilter regex: %w", err) @@ -171,7 +171,7 @@ func NewProxySetProvider(name string, interval time.Duration, filter string, exc healthCheck: hc, } - fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, excludeFilter, excludeTypeArray, filterRegs, excludeFilterReg), proxiesOnUpdate(pd)) + fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, excludeFilter, excludeTypeArray, filterRegs, excludeFilterReg, dialerProxy), proxiesOnUpdate(pd)) pd.Fetcher = fetcher wrapper := &ProxySetProvider{pd} runtime.SetFinalizer(wrapper, stopProxyProvider) @@ -267,7 +267,7 @@ func proxiesOnUpdate(pd *proxySetProvider) func([]C.Proxy) { } } -func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray []string, filterRegs []*regexp2.Regexp, excludeFilterReg *regexp2.Regexp) resource.Parser[[]C.Proxy] { +func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray []string, filterRegs []*regexp2.Regexp, excludeFilterReg *regexp2.Regexp, dialerProxy string) resource.Parser[[]C.Proxy] { return func(buf []byte) ([]C.Proxy, error) { schema := &ProxySchema{} @@ -330,6 +330,9 @@ func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray if _, ok := proxiesSet[name]; ok { continue } + if len(dialerProxy) > 0 { + mapping["dialer-proxy"] = dialerProxy + } proxy, err := adapter.ParseProxy(mapping) if err != nil { return nil, fmt.Errorf("proxy %d error: %w", idx, err) From 17922dc85798b30d7e478f939cf60848f4f506bb Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 12 Apr 2023 11:09:31 +0800 Subject: [PATCH 43/88] chore: proxyDialer first using old function to let mux work --- component/proxydialer/proxydialer.go | 44 ++++++++-------------------- dns/util.go | 16 +++++----- 2 files changed, 20 insertions(+), 40 deletions(-) diff --git a/component/proxydialer/proxydialer.go b/component/proxydialer/proxydialer.go index 7fac628f..a32e54d1 100644 --- a/component/proxydialer/proxydialer.go +++ b/component/proxydialer/proxydialer.go @@ -45,23 +45,13 @@ func (p proxyDialer) DialContext(ctx context.Context, network, address string) ( return N.NewBindPacketConn(pc, currentMeta.UDPAddr()), nil } var conn C.Conn - switch p.proxy.SupportWithDialer() { - case C.ALLNet: - fallthrough - case C.TCP: + if d, ok := p.dialer.(dialer.Dialer); ok { // first using old function to let mux work + conn, err = p.proxy.DialContext(ctx, currentMeta, dialer.WithOption(d.Opt)) + } else { conn, err = p.proxy.DialContextWithDialer(ctx, p.dialer, currentMeta) - if err != nil { - return nil, err - } - default: // fallback to old function - if d, ok := p.dialer.(dialer.Dialer); ok { // fallback to old function - conn, err = p.proxy.DialContext(ctx, currentMeta, dialer.WithOption(d.Opt)) - if err != nil { - return nil, err - } - } else { - return nil, C.ErrNotSupport - } + } + if err != nil { + return nil, err } if p.statistic { conn = statistic.NewTCPTracker(conn, statistic.DefaultManager, currentMeta, nil, 0, 0, false) @@ -81,23 +71,13 @@ func (p proxyDialer) listenPacket(ctx context.Context, currentMeta *C.Metadata) var pc C.PacketConn var err error currentMeta.NetWork = C.UDP - switch p.proxy.SupportWithDialer() { - case C.ALLNet: - fallthrough - case C.UDP: + if d, ok := p.dialer.(dialer.Dialer); ok { // first using old function to let mux work + pc, err = p.proxy.ListenPacketContext(ctx, currentMeta, dialer.WithOption(d.Opt)) + } else { pc, err = p.proxy.ListenPacketWithDialer(ctx, p.dialer, currentMeta) - if err != nil { - return nil, err - } - default: // fallback to old function - if d, ok := p.dialer.(dialer.Dialer); ok { // fallback to old function - pc, err = p.proxy.ListenPacketContext(ctx, currentMeta, dialer.WithOption(d.Opt)) - if err != nil { - return nil, err - } - } else { - return nil, C.ErrNotSupport - } + } + if err != nil { + return nil, err } if p.statistic { pc = statistic.NewUDPTracker(pc, statistic.DefaultManager, currentMeta, nil, 0, 0, false) diff --git a/dns/util.go b/dns/util.go index 9df4482b..bfd2e9ed 100644 --- a/dns/util.go +++ b/dns/util.go @@ -170,15 +170,15 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, Host: host, DstPort: port, } - if proxyAdapter.IsL3Protocol(metadata) { - dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r) - if err != nil { - return nil, err - } - metadata.Host = "" - metadata.DstIP = dstIP - } if proxyAdapter != nil { + if proxyAdapter.IsL3Protocol(metadata) { // L3 proxy should resolve domain before to avoid loopback + dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r) + if err != nil { + return nil, err + } + metadata.Host = "" + metadata.DstIP = dstIP + } return proxyAdapter.DialContext(ctx, metadata, opts...) } opts = append(opts, dialer.WithResolver(r)) From 20eb168315f9012508baef9d1a9e93d9e018498b Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 12 Apr 2023 12:49:53 +0800 Subject: [PATCH 44/88] fix: proxyDialer panic when domain name was not resolved --- adapter/outbound/vmess.go | 6 ++--- component/proxydialer/proxydialer.go | 39 +++++++--------------------- constant/metadata.go | 18 +++++++++++++ transport/tuic/conn.go | 5 ++-- transport/tuic/protocol.go | 12 +++++++++ 5 files changed, 45 insertions(+), 35 deletions(-) diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index c5212eef..710e19bb 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -507,9 +507,9 @@ type vmessPacketConn struct { // WriteTo implments C.PacketConn.WriteTo // Since VMess doesn't support full cone NAT by design, we verify if addr matches uc.rAddr, and drop the packet if not. func (uc *vmessPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) { - allowedAddr := uc.rAddr.(*net.UDPAddr) - destAddr := addr.(*net.UDPAddr) - if !(allowedAddr.IP.Equal(destAddr.IP) && allowedAddr.Port == destAddr.Port) { + allowedAddr := uc.rAddr + destAddr := addr + if allowedAddr.String() != destAddr.String() { return 0, ErrUDPRemoteAddrMismatch } uc.access.Lock() diff --git a/component/proxydialer/proxydialer.go b/component/proxydialer/proxydialer.go index a32e54d1..8a3ab263 100644 --- a/component/proxydialer/proxydialer.go +++ b/component/proxydialer/proxydialer.go @@ -33,8 +33,8 @@ func NewByName(proxyName string, dialer C.Dialer) (C.Dialer, error) { } func (p proxyDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { - currentMeta, err := addrToMetadata(address) - if err != nil { + currentMeta := &C.Metadata{Type: C.INNER} + if err := currentMeta.SetRemoteAddress(address); err != nil { return nil, err } if strings.Contains(network, "udp") { // using in wireguard outbound @@ -42,9 +42,14 @@ func (p proxyDialer) DialContext(ctx context.Context, network, address string) ( if err != nil { return nil, err } - return N.NewBindPacketConn(pc, currentMeta.UDPAddr()), nil + var rAddr net.Addr = currentMeta.UDPAddr() + if rAddr == nil { // the domain name was not resolved, will appear in not stream-oriented udp like Shadowsocks/Tuic + rAddr = N.NewCustomAddr("udp", currentMeta.RemoteAddress(), nil) + } + return N.NewBindPacketConn(pc, rAddr), nil } var conn C.Conn + var err error if d, ok := p.dialer.(dialer.Dialer); ok { // first using old function to let mux work conn, err = p.proxy.DialContext(ctx, currentMeta, dialer.WithOption(d.Opt)) } else { @@ -60,8 +65,8 @@ func (p proxyDialer) DialContext(ctx context.Context, network, address string) ( } func (p proxyDialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) { - currentMeta, err := addrToMetadata(rAddrPort.String()) - if err != nil { + currentMeta := &C.Metadata{Type: C.INNER} + if err := currentMeta.SetRemoteAddress(address); err != nil { return nil, err } return p.listenPacket(ctx, currentMeta) @@ -84,27 +89,3 @@ func (p proxyDialer) listenPacket(ctx context.Context, currentMeta *C.Metadata) } return pc, nil } - -func addrToMetadata(rawAddress string) (addr *C.Metadata, err error) { - host, port, err := net.SplitHostPort(rawAddress) - if err != nil { - err = fmt.Errorf("addrToMetadata failed: %w", err) - return - } - - if ip, err := netip.ParseAddr(host); err != nil { - addr = &C.Metadata{ - Host: host, - DstPort: port, - } - } else { - addr = &C.Metadata{ - Host: "", - DstIP: ip.Unmap(), - DstPort: port, - } - } - addr.Type = C.INNER - - return -} diff --git a/constant/metadata.go b/constant/metadata.go index 4ff20305..1c344d5d 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -229,3 +229,21 @@ func (m *Metadata) String() string { func (m *Metadata) Valid() bool { return m.Host != "" || m.DstIP.IsValid() } + +func (m *Metadata) SetRemoteAddress(rawAddress string) error { + host, port, err := net.SplitHostPort(rawAddress) + if err != nil { + return err + } + + if ip, err := netip.ParseAddr(host); err != nil { + m.Host = host + m.DstIP = netip.Addr{} + } else { + m.Host = "" + m.DstIP = ip.Unmap() + } + m.DstPort = port + + return nil +} diff --git a/transport/tuic/conn.go b/transport/tuic/conn.go index 8f63da75..567f6ce5 100644 --- a/transport/tuic/conn.go +++ b/transport/tuic/conn.go @@ -2,7 +2,6 @@ package tuic import ( "net" - "net/netip" "sync" "sync/atomic" "time" @@ -216,11 +215,11 @@ func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err erro } buf := pool.GetBuffer() defer pool.PutBuffer(buf) - addrPort, err := netip.ParseAddrPort(addr.String()) + address, err := NewAddressNetAddr(addr) if err != nil { return } - err = NewPacket(q.connId, uint16(len(p)), NewAddressAddrPort(addrPort), p).WriteTo(buf) + err = NewPacket(q.connId, uint16(len(p)), address, p).WriteTo(buf) if err != nil { return } diff --git a/transport/tuic/protocol.go b/transport/tuic/protocol.go index 570b6e54..a460eecc 100644 --- a/transport/tuic/protocol.go +++ b/transport/tuic/protocol.go @@ -464,6 +464,18 @@ func NewAddress(metadata *C.Metadata) Address { } } +func NewAddressNetAddr(addr net.Addr) (Address, error) { + addrStr := addr.String() + if addrPort, err := netip.ParseAddrPort(addrStr); err == nil { + return NewAddressAddrPort(addrPort), nil + } + metadata := &C.Metadata{} + if err := metadata.SetRemoteAddress(addrStr); err != nil { + return Address{}, err + } + return NewAddress(metadata), nil +} + func NewAddressAddrPort(addrPort netip.AddrPort) Address { var addrType byte port := addrPort.Port() From e745755a4601098973efc846d66110b11abccbde Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 12 Apr 2023 12:57:59 +0800 Subject: [PATCH 45/88] fix: direct outbound not ensure ip was resolved --- adapter/outbound/direct.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go index cf1b2648..eae37d7a 100644 --- a/adapter/outbound/direct.go +++ b/adapter/outbound/direct.go @@ -2,6 +2,7 @@ package outbound import ( "context" + "errors" "net" "github.com/Dreamacro/clash/component/dialer" @@ -26,7 +27,14 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ... // ListenPacketContext implements C.ProxyAdapter func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { - opts = append(opts, dialer.WithResolver(resolver.DefaultResolver)) + // net.UDPConn.WriteTo only working with *net.UDPAddr, so we need a net.UDPAddr + if !metadata.Resolved() { + ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, resolver.DefaultResolver) + if err != nil { + return nil, errors.New("can't resolve ip") + } + metadata.DstIP = ip + } pc, err := dialer.ListenPacket(ctx, dialer.ParseNetwork("udp", metadata.DstIP), "", d.Base.DialOptions(opts...)...) if err != nil { return nil, err From 836615aac9643bdf5a0368217caf62d7fc88f6a9 Mon Sep 17 00:00:00 2001 From: H1JK Date: Wed, 12 Apr 2023 14:40:38 +0800 Subject: [PATCH 46/88] fix: Converter panic on bad VMess share links --- common/buf/sing.go | 16 ++++++++++------ common/convert/converter.go | 15 ++++++++++----- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/common/buf/sing.go b/common/buf/sing.go index f86b5755..c176ecb1 100644 --- a/common/buf/sing.go +++ b/common/buf/sing.go @@ -9,10 +9,12 @@ const BufferSize = buf.BufferSize type Buffer = buf.Buffer -var New = buf.New -var StackNew = buf.StackNew -var StackNewSize = buf.StackNewSize -var With = buf.With +var ( + New = buf.New + StackNew = buf.StackNew + StackNewSize = buf.StackNewSize + With = buf.With +) var KeepAlive = common.KeepAlive @@ -21,5 +23,7 @@ func Dup[T any](obj T) T { return common.Dup(obj) } -var Must = common.Must -var Error = common.Error +var ( + Must = common.Must + Error = common.Error +) diff --git a/common/convert/converter.go b/common/convert/converter.go index abd07a94..b67918db 100644 --- a/common/convert/converter.go +++ b/common/convert/converter.go @@ -5,10 +5,11 @@ import ( "encoding/base64" "encoding/json" "fmt" - "github.com/Dreamacro/clash/log" "net/url" "strconv" "strings" + + "github.com/Dreamacro/clash/log" ) // ConvertsV2Ray convert V2Ray subscribe proxies data to clash proxies config @@ -201,7 +202,8 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { vmess["servername"] = sni } - network := strings.ToLower(values["net"].(string)) + network, _ := values["net"].(string) + network = strings.ToLower(network) if values["type"] == "http" { network = "http" } else if network == "http" { @@ -209,9 +211,12 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { } vmess["network"] = network - tls := strings.ToLower(values["tls"].(string)) - if strings.HasSuffix(tls, "tls") { - vmess["tls"] = true + tls, ok := values["tls"].(string) + if ok { + tls = strings.ToLower(tls) + if strings.HasSuffix(tls, "tls") { + vmess["tls"] = true + } } switch network { From 42721f3b758b411ca22dec4e1e022374813c5b88 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 12 Apr 2023 18:19:59 +0800 Subject: [PATCH 47/88] fix: proxyDialer has a non-nil interface containing nil pointer judgment --- component/proxydialer/proxydialer.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/component/proxydialer/proxydialer.go b/component/proxydialer/proxydialer.go index 8a3ab263..e215ff0f 100644 --- a/component/proxydialer/proxydialer.go +++ b/component/proxydialer/proxydialer.go @@ -42,8 +42,10 @@ func (p proxyDialer) DialContext(ctx context.Context, network, address string) ( if err != nil { return nil, err } - var rAddr net.Addr = currentMeta.UDPAddr() - if rAddr == nil { // the domain name was not resolved, will appear in not stream-oriented udp like Shadowsocks/Tuic + var rAddr net.Addr + if udpAddr := currentMeta.UDPAddr(); udpAddr != nil { + rAddr = udpAddr + } else { // the domain name was not resolved, will appear in not stream-oriented udp like Shadowsocks/Tuic rAddr = N.NewCustomAddr("udp", currentMeta.RemoteAddress(), nil) } return N.NewBindPacketConn(pc, rAddr), nil From 5d7fd47cf9e314f3ac3efd9f64a6b51cc0e2f434 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 12 Apr 2023 18:27:22 +0800 Subject: [PATCH 48/88] fix: CGO build failed on darwin-10.16 --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index e7ceadf9..d6479643 100644 --- a/go.mod +++ b/go.mod @@ -87,7 +87,7 @@ require ( github.com/quic-go/qtls-go1-19 v0.2.1 // indirect github.com/quic-go/qtls-go1-20 v0.1.1 // indirect github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect - github.com/shoenig/go-m1cpu v0.1.4 // indirect + github.com/shoenig/go-m1cpu v0.1.5 // indirect github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e // indirect diff --git a/go.sum b/go.sum index 38a2e16c..7b4d8379 100644 --- a/go.sum +++ b/go.sum @@ -160,8 +160,9 @@ github.com/samber/lo v1.37.0 h1:XjVcB8g6tgUp8rsPsJ2CvhClfImrpL04YpQHXeHPhRw= github.com/samber/lo v1.37.0/go.mod h1:9vaz2O4o8oOnK23pd2TrXufcbdbJIa3b6cstBWKpopA= github.com/shirou/gopsutil/v3 v3.23.3 h1:Syt5vVZXUDXPEXpIBt5ziWsJ4LdSAAxF4l/xZeQgSEE= github.com/shirou/gopsutil/v3 v3.23.3/go.mod h1:lSBNN6t3+D6W5e5nXTxc8KIMMVxAcS+6IJlffjRRlMU= -github.com/shoenig/go-m1cpu v0.1.4 h1:SZPIgRM2sEF9NJy50mRHu9PKGwxyyTTJIWvCtgVbozs= github.com/shoenig/go-m1cpu v0.1.4/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ= +github.com/shoenig/go-m1cpu v0.1.5 h1:LF57Z/Fpb/WdGLjt2HZilNnmZOxg/q2bSKTQhgbrLrQ= +github.com/shoenig/go-m1cpu v0.1.5/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ= github.com/shoenig/test v0.6.3 h1:GVXWJFk9PiOjN0KoJ7VrJGH6uLPnqxR7/fe3HUPfE0c= github.com/shoenig/test v0.6.3/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b h1:rXHg9GrUEtWZhEkrykicdND3VPjlVbYiLdX9J7gimS8= From aaf534427eac7470999878d6e3e7b267a37b0b0c Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 12 Apr 2023 18:50:51 +0800 Subject: [PATCH 49/88] chore: close all connections after proxySet initial --- adapter/provider/provider.go | 14 ++++++++++++++ tunnel/statistic/tracker.go | 1 + 2 files changed, 15 insertions(+) diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index 96852c4b..4138c0de 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -17,6 +17,7 @@ import ( C "github.com/Dreamacro/clash/constant" types "github.com/Dreamacro/clash/constant/provider" "github.com/Dreamacro/clash/log" + "github.com/Dreamacro/clash/tunnel/statistic" "github.com/dlclark/regexp2" "gopkg.in/yaml.v3" @@ -81,6 +82,7 @@ func (pp *proxySetProvider) Initial() error { } pp.OnUpdate(elm) pp.getSubscriptionInfo() + pp.closeAllConnections() return nil } @@ -138,6 +140,18 @@ func (pp *proxySetProvider) getSubscriptionInfo() { }() } +func (pp *proxySetProvider) closeAllConnections() { + snapshot := statistic.DefaultManager.Snapshot() + for _, c := range snapshot.Connections { + for _, chain := range c.Chains() { + if chain == pp.Name() { + _ = c.Close() + break + } + } + } +} + func stopProxyProvider(pd *ProxySetProvider) { pd.healthCheck.close() _ = pd.Fetcher.Destroy() diff --git a/tunnel/statistic/tracker.go b/tunnel/statistic/tracker.go index 3678a19d..96376eb4 100644 --- a/tunnel/statistic/tracker.go +++ b/tunnel/statistic/tracker.go @@ -15,6 +15,7 @@ import ( type tracker interface { ID() string Close() error + C.Connection } type trackerInfo struct { From cb0c9e5caff31eab24943734dd4c34e7de28260d Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 12 Apr 2023 21:49:22 +0800 Subject: [PATCH 50/88] chore: udp always direct pass ip to remote without domain --- adapter/outbound/shadowsocks.go | 21 ++++---- adapter/outbound/vless.go | 72 ++++++++++++++++++---------- adapter/outbound/vmess.go | 70 ++++++++++++++------------- component/proxydialer/proxydialer.go | 17 ++++--- 4 files changed, 106 insertions(+), 74 deletions(-) diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 14bd96b0..e2ed24c2 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -12,6 +12,7 @@ import ( "github.com/Dreamacro/clash/common/structure" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/proxydialer" + "github.com/Dreamacro/clash/component/resolver" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/transport/restls" obfs "github.com/Dreamacro/clash/transport/simple-obfs" @@ -184,12 +185,7 @@ func (ss *ShadowSocks) ListenPacketWithDialer(ctx context.Context, dialer C.Dial if err != nil { return nil, err } - destination := M.ParseSocksaddr(metadata.RemoteAddress()) - if ss.option.UDPOverTCPVersion == 1 { - return newPacketConn(uot.NewConn(tcpConn, uot.Request{Destination: destination}), ss), nil - } else { - return newPacketConn(uot.NewLazyConn(tcpConn, uot.Request{Destination: destination}), ss), nil - } + return ss.ListenPacketOnStreamConn(ctx, tcpConn, metadata) } addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ss.addr, ss.prefer) if err != nil { @@ -210,9 +206,18 @@ func (ss *ShadowSocks) SupportWithDialer() C.NetWork { } // ListenPacketOnStreamConn implements C.ProxyAdapter -func (ss *ShadowSocks) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { +func (ss *ShadowSocks) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { if ss.option.UDPOverTCP { - destination := M.ParseSocksaddr(metadata.RemoteAddress()) + // ss uot use stream-oriented udp with a special address, so we need a net.UDPAddr + if !metadata.Resolved() { + ip, err := resolver.ResolveIP(ctx, metadata.Host) + if err != nil { + return nil, errors.New("can't resolve ip") + } + metadata.DstIP = ip + } + + destination := M.SocksaddrFromNet(metadata.UDPAddr()) if ss.option.UDPOverTCPVersion == uot.LegacyVersion { return newPacketConn(uot.NewConn(c, uot.Request{Destination: destination}), ss), nil } else { diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index b7a22245..ecfa1a13 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -169,7 +169,34 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { return nil, err } - return v.client.StreamConn(c, parseVlessAddr(metadata, v.option.XUDP)) + return v.streamConn(c, metadata) +} + +func (v *Vless) streamConn(c net.Conn, metadata *C.Metadata) (conn net.Conn, err error) { + metadata = &C.Metadata{ // a clear metadata only contains ip + NetWork: metadata.NetWork, + DstIP: metadata.DstIP, + DstPort: metadata.DstPort, + } + if metadata.NetWork == C.UDP { + if v.option.PacketAddr { + metadata = &C.Metadata{ + NetWork: C.UDP, + Host: packetaddr.SeqPacketMagicAddress, + DstPort: "443", + } + } + conn, err = v.client.StreamConn(c, parseVlessAddr(metadata, v.option.XUDP)) + if v.option.PacketAddr { + conn = packetaddr.NewBindConn(conn) + } + } else { + conn, err = v.client.StreamConn(c, parseVlessAddr(metadata, false)) + } + if err != nil { + conn = nil + } + return } func (v *Vless) streamTLSOrXTLSConn(conn net.Conn, isH2 bool) (net.Conn, error) { @@ -271,7 +298,6 @@ func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o } metadata.DstIP = ip } - var c net.Conn // gun transport if v.transport != nil && len(opts) == 0 { @@ -283,21 +309,12 @@ func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o safeConnClose(c, err) }(c) - if v.option.PacketAddr { - packetAddrMetadata := *metadata // make a copy - packetAddrMetadata.Host = packetaddr.SeqPacketMagicAddress - packetAddrMetadata.DstPort = "443" - - c, err = v.client.StreamConn(c, parseVlessAddr(&packetAddrMetadata, false)) - } else { - c, err = v.client.StreamConn(c, parseVlessAddr(metadata, v.option.XUDP)) - } - + c, err = v.streamConn(c, metadata) if err != nil { return nil, fmt.Errorf("new vless client error: %v", err) } - return v.ListenPacketOnStreamConn(c, metadata) + return v.ListenPacketOnStreamConn(ctx, c, metadata) } return v.ListenPacketWithDialer(ctx, dialer.NewDialer(v.Base.DialOptions(opts...)...), metadata) } @@ -310,6 +327,7 @@ func (v *Vless) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met return nil, err } } + // vless use stream-oriented udp with a special address, so we need a net.UDPAddr if !metadata.Resolved() { ip, err := resolver.ResolveIP(ctx, metadata.Host) @@ -318,6 +336,7 @@ func (v *Vless) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met } metadata.DstIP = ip } + c, err := dialer.DialContext(ctx, "tcp", v.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) @@ -327,21 +346,12 @@ func (v *Vless) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met safeConnClose(c, err) }(c) - if v.option.PacketAddr { - packetAddrMetadata := *metadata // make a copy - packetAddrMetadata.Host = packetaddr.SeqPacketMagicAddress - packetAddrMetadata.DstPort = "443" - - c, err = v.StreamConn(c, &packetAddrMetadata) - } else { - c, err = v.StreamConn(c, metadata) - } - + c, err = v.streamConn(c, metadata) if err != nil { return nil, fmt.Errorf("new vless client error: %v", err) } - return v.ListenPacketOnStreamConn(c, metadata) + return v.ListenPacketOnStreamConn(ctx, c, metadata) } // SupportWithDialer implements C.ProxyAdapter @@ -350,7 +360,16 @@ func (v *Vless) SupportWithDialer() C.NetWork { } // ListenPacketOnStreamConn implements C.ProxyAdapter -func (v *Vless) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { +func (v *Vless) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { + // vless use stream-oriented udp with a special address, so we need a net.UDPAddr + if !metadata.Resolved() { + ip, err := resolver.ResolveIP(ctx, metadata.Host) + if err != nil { + return nil, errors.New("can't resolve ip") + } + metadata.DstIP = ip + } + if v.option.XUDP { return newPacketConn(&threadSafePacketConn{ PacketConn: vmessSing.NewXUDPConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), @@ -517,6 +536,9 @@ func NewVless(option VlessOption) (*Vless, error) { option.XUDP = true } } + if option.XUDP { + option.PacketAddr = false + } client, err := vless.NewClient(option.UUID, addons, option.FlowShow) if err != nil { diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index 710e19bb..8901f3d5 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -217,27 +217,42 @@ func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { if err != nil { return nil, err } + return v.streamConn(c, metadata) +} + +func (v *Vmess) streamConn(c net.Conn, metadata *C.Metadata) (conn net.Conn, err error) { if metadata.NetWork == C.UDP { if v.option.XUDP { if N.NeedHandshake(c) { - return v.client.DialEarlyXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil + conn = v.client.DialEarlyXUDPPacketConn(c, M.SocksaddrFromNet(metadata.UDPAddr())) } else { - return v.client.DialXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) + conn, err = v.client.DialXUDPPacketConn(c, M.SocksaddrFromNet(metadata.UDPAddr())) } + } else if v.option.PacketAddr { + if N.NeedHandshake(c) { + conn = v.client.DialEarlyPacketConn(c, M.ParseSocksaddrHostPort(packetaddr.SeqPacketMagicAddress, 443)) + } else { + conn, err = v.client.DialPacketConn(c, M.ParseSocksaddrHostPort(packetaddr.SeqPacketMagicAddress, 443)) + } + conn = packetaddr.NewBindConn(conn) } else { if N.NeedHandshake(c) { - return v.client.DialEarlyPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil + conn = v.client.DialEarlyPacketConn(c, M.SocksaddrFromNet(metadata.UDPAddr())) } else { - return v.client.DialPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) + conn, err = v.client.DialPacketConn(c, M.SocksaddrFromNet(metadata.UDPAddr())) } } } else { if N.NeedHandshake(c) { - return v.client.DialEarlyConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), nil + conn = v.client.DialEarlyConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) } else { - return v.client.DialConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) + conn, err = v.client.DialConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) } } + if err != nil { + conn = nil + } + return } // DialContext implements C.ProxyAdapter @@ -293,14 +308,6 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o } metadata.DstIP = ip } - - if v.option.PacketAddr { - _metadata := *metadata // make a copy - metadata = &_metadata - metadata.Host = packetaddr.SeqPacketMagicAddress - metadata.DstPort = "443" - } - var c net.Conn // gun transport if v.transport != nil && len(opts) == 0 { @@ -312,24 +319,11 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o safeConnClose(c, err) }(c) - if v.option.XUDP { - if N.NeedHandshake(c) { - c = v.client.DialEarlyXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) - } else { - c, err = v.client.DialXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) - } - } else { - if N.NeedHandshake(c) { - c = v.client.DialEarlyPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) - } else { - c, err = v.client.DialPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) - } - } - + c, err = v.streamConn(c, metadata) if err != nil { return nil, fmt.Errorf("new vmess client error: %v", err) } - return v.ListenPacketOnStreamConn(c, metadata) + return v.ListenPacketOnStreamConn(ctx, c, metadata) } return v.ListenPacketWithDialer(ctx, dialer.NewDialer(v.Base.DialOptions(opts...)...), metadata) } @@ -342,6 +336,7 @@ func (v *Vmess) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met return nil, err } } + // vmess use stream-oriented udp with a special address, so we need a net.UDPAddr if !metadata.Resolved() { ip, err := resolver.ResolveIP(ctx, metadata.Host) @@ -364,7 +359,7 @@ func (v *Vmess) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met if err != nil { return nil, fmt.Errorf("new vmess client error: %v", err) } - return v.ListenPacketOnStreamConn(c, metadata) + return v.ListenPacketOnStreamConn(ctx, c, metadata) } // SupportWithDialer implements C.ProxyAdapter @@ -373,10 +368,17 @@ func (v *Vmess) SupportWithDialer() C.NetWork { } // ListenPacketOnStreamConn implements C.ProxyAdapter -func (v *Vmess) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { - if v.option.PacketAddr { - return newPacketConn(&threadSafePacketConn{PacketConn: packetaddr.NewBindConn(c)}, v), nil - } else if pc, ok := c.(net.PacketConn); ok { +func (v *Vmess) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { + // vmess use stream-oriented udp with a special address, so we need a net.UDPAddr + if !metadata.Resolved() { + ip, err := resolver.ResolveIP(ctx, metadata.Host) + if err != nil { + return nil, errors.New("can't resolve ip") + } + metadata.DstIP = ip + } + + if pc, ok := c.(net.PacketConn); ok { return newPacketConn(&threadSafePacketConn{PacketConn: pc}, v), nil } return newPacketConn(&vmessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil diff --git a/component/proxydialer/proxydialer.go b/component/proxydialer/proxydialer.go index e215ff0f..fc5cb294 100644 --- a/component/proxydialer/proxydialer.go +++ b/component/proxydialer/proxydialer.go @@ -2,6 +2,7 @@ package proxydialer import ( "context" + "errors" "fmt" "net" "net/netip" @@ -9,6 +10,7 @@ import ( N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/component/dialer" + "github.com/Dreamacro/clash/component/resolver" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/tunnel" "github.com/Dreamacro/clash/tunnel/statistic" @@ -38,17 +40,18 @@ func (p proxyDialer) DialContext(ctx context.Context, network, address string) ( return nil, err } if strings.Contains(network, "udp") { // using in wireguard outbound + if !currentMeta.Resolved() { + ip, err := resolver.ResolveIP(ctx, currentMeta.Host) + if err != nil { + return nil, errors.New("can't resolve ip") + } + currentMeta.DstIP = ip + } pc, err := p.listenPacket(ctx, currentMeta) if err != nil { return nil, err } - var rAddr net.Addr - if udpAddr := currentMeta.UDPAddr(); udpAddr != nil { - rAddr = udpAddr - } else { // the domain name was not resolved, will appear in not stream-oriented udp like Shadowsocks/Tuic - rAddr = N.NewCustomAddr("udp", currentMeta.RemoteAddress(), nil) - } - return N.NewBindPacketConn(pc, rAddr), nil + return N.NewBindPacketConn(pc, currentMeta.UDPAddr()), nil } var conn C.Conn var err error From cd42e9832c4caae5824a76f93325210667049329 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 12 Apr 2023 22:06:21 +0800 Subject: [PATCH 51/88] chore: resolver priority return TypeA in ResolveIP (not effected LookupIP) --- component/dialer/dialer.go | 13 +------------ component/resolver/resolver.go | 22 ++++++++++++++++----- dns/resolver.go | 35 ---------------------------------- 3 files changed, 18 insertions(+), 52 deletions(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index f6dfaf86..5a55cd22 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -157,7 +157,7 @@ func concurrentDualStackDialContext(ctx context.Context, network string, ips []n } func dualStackDialContext(ctx context.Context, dialFn dialFunc, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error) { - ipv4s, ipv6s := sortationAddr(ips) + ipv4s, ipv6s := resolver.SortationAddr(ips) preferIPVersion := opt.prefer fallbackTicker := time.NewTicker(fallbackTimeout) @@ -309,17 +309,6 @@ func parseAddr(ctx context.Context, network, address string, preferResolver reso return ips, port, nil } -func sortationAddr(ips []netip.Addr) (ipv4s, ipv6s []netip.Addr) { - for _, v := range ips { - if v.Is4() { // 4in6 parse was in parseAddr - ipv4s = append(ipv4s, v) - } else { - ipv6s = append(ipv6s, v) - } - } - return -} - type Dialer struct { Opt option } diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index 3b5426d8..6be6a95f 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -44,9 +44,6 @@ type Resolver interface { LookupIP(ctx context.Context, host string) (ips []netip.Addr, err error) LookupIPv4(ctx context.Context, host string) (ips []netip.Addr, err error) LookupIPv6(ctx context.Context, host string) (ips []netip.Addr, err error) - ResolveIP(ctx context.Context, host string) (ip netip.Addr, err error) - ResolveIPv4(ctx context.Context, host string) (ip netip.Addr, err error) - ResolveIPv6(ctx context.Context, host string) (ip netip.Addr, err error) ExchangeContext(ctx context.Context, m *dns.Msg) (msg *dns.Msg, err error) Invalid() bool } @@ -201,10 +198,14 @@ func ResolveIPWithResolver(ctx context.Context, host string, r Resolver) (netip. } else if len(ips) == 0 { return netip.Addr{}, fmt.Errorf("%w: %s", ErrIPNotFound, host) } - return ips[fastrand.Intn(len(ips))], nil + ipv4s, ipv6s := SortationAddr(ips) + if len(ipv4s) > 0 { + return ipv4s[fastrand.Intn(len(ipv4s))], nil + } + return ipv6s[fastrand.Intn(len(ipv6s))], nil } -// ResolveIP with a host, return ip +// ResolveIP with a host, return ip and priority return TypeA func ResolveIP(ctx context.Context, host string) (netip.Addr, error) { return ResolveIPWithResolver(ctx, host, DefaultResolver) } @@ -265,3 +266,14 @@ func LookupIPProxyServerHost(ctx context.Context, host string) ([]netip.Addr, er } return LookupIP(ctx, host) } + +func SortationAddr(ips []netip.Addr) (ipv4s, ipv6s []netip.Addr) { + for _, v := range ips { + if v.Unmap().Is4() { + ipv4s = append(ipv4s, v) + } else { + ipv6s = append(ipv6s, v) + } + } + return +} diff --git a/dns/resolver.go b/dns/resolver.go index 467e9f14..df8ed3d1 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -3,7 +3,6 @@ package dns import ( "context" "errors" - "fmt" "net/netip" "strings" "time" @@ -20,7 +19,6 @@ import ( "github.com/Dreamacro/clash/log" D "github.com/miekg/dns" - "github.com/zhangyunhao116/fastrand" "golang.org/x/sync/singleflight" ) @@ -119,49 +117,16 @@ func (r *Resolver) LookupIP(ctx context.Context, host string) (ips []netip.Addr, return ips, nil } -// ResolveIP request with TypeA and TypeAAAA, priority return TypeA -func (r *Resolver) ResolveIP(ctx context.Context, host string) (ip netip.Addr, err error) { - ips, err := r.LookupIPPrimaryIPv4(ctx, host) - if err != nil { - return netip.Addr{}, err - } else if len(ips) == 0 { - return netip.Addr{}, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, host) - } - return ips[fastrand.Intn(len(ips))], nil -} - // LookupIPv4 request with TypeA func (r *Resolver) LookupIPv4(ctx context.Context, host string) ([]netip.Addr, error) { return r.lookupIP(ctx, host, D.TypeA) } -// ResolveIPv4 request with TypeA -func (r *Resolver) ResolveIPv4(ctx context.Context, host string) (ip netip.Addr, err error) { - ips, err := r.lookupIP(ctx, host, D.TypeA) - if err != nil { - return netip.Addr{}, err - } else if len(ips) == 0 { - return netip.Addr{}, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, host) - } - return ips[fastrand.Intn(len(ips))], nil -} - // LookupIPv6 request with TypeAAAA func (r *Resolver) LookupIPv6(ctx context.Context, host string) ([]netip.Addr, error) { return r.lookupIP(ctx, host, D.TypeAAAA) } -// ResolveIPv6 request with TypeAAAA -func (r *Resolver) ResolveIPv6(ctx context.Context, host string) (ip netip.Addr, err error) { - ips, err := r.lookupIP(ctx, host, D.TypeAAAA) - if err != nil { - return netip.Addr{}, err - } else if len(ips) == 0 { - return netip.Addr{}, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, host) - } - return ips[fastrand.Intn(len(ips))], nil -} - func (r *Resolver) shouldIPFallback(ip netip.Addr) bool { for _, filter := range r.fallbackIPFilters { if filter.Match(ip) { From dbffbca6f5c26d3e5ab61d340f51928af6013e3a Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 12 Apr 2023 23:55:51 +0800 Subject: [PATCH 52/88] doc: update config.yaml --- docs/config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/config.yaml b/docs/config.yaml index 99f40ce7..922d4895 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -686,7 +686,7 @@ proxies: # socks5 proxy-groups: # 代理链,目前relay可以支持udp的只有vmess/vless/trojan/ss/ssr/tuic - # wireguard目前不支持在relay中使用,请使用tunnels功能模拟 + # wireguard目前不支持在relay中使用,请使用proxy中的dialer-proxy配置项 # Traffic: clash <-> http <-> vmess <-> ss1 <-> ss2 <-> Internet - name: "relay" type: relay From 38a85272c26658b1e5c5ea5fd8f5b75ddbc7ed7b Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 13 Apr 2023 11:10:35 +0800 Subject: [PATCH 53/88] fix: vless tcp not working --- adapter/outbound/vless.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index ecfa1a13..5f7c2087 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -173,12 +173,12 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { } func (v *Vless) streamConn(c net.Conn, metadata *C.Metadata) (conn net.Conn, err error) { - metadata = &C.Metadata{ // a clear metadata only contains ip - NetWork: metadata.NetWork, - DstIP: metadata.DstIP, - DstPort: metadata.DstPort, - } if metadata.NetWork == C.UDP { + metadata = &C.Metadata{ // a clear metadata only contains ip + NetWork: metadata.NetWork, + DstIP: metadata.DstIP, + DstPort: metadata.DstPort, + } if v.option.PacketAddr { metadata = &C.Metadata{ NetWork: C.UDP, From 394889258cf2a0023369494f0f5a3e164fd41fe1 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 13 Apr 2023 17:01:01 +0800 Subject: [PATCH 54/88] fix: wireguard reconnect failed --- adapter/outbound/wireguard.go | 10 +++++++++- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index a2634be4..f96f2d6f 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -174,7 +174,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { connectAddr = option.Addr() } } - outbound.bind = wireguard.NewClientBind(context.Background(), outbound.dialer, isConnect, connectAddr, reserved) + outbound.bind = wireguard.NewClientBind(context.Background(), outbound, outbound.dialer, isConnect, connectAddr, reserved) var localPrefixes []netip.Prefix @@ -329,6 +329,14 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { return outbound, nil } +func (w *WireGuard) NewError(ctx context.Context, err error) { + if E.IsClosedOrCanceled(err) { + log.SingLogger.Debug(fmt.Sprintf("[WG](%s) connection closed: %s", w.Name(), err)) + return + } + log.SingLogger.Error(fmt.Sprintf("[WG](%s) %s", w.Name(), err)) +} + func closeWireGuard(w *WireGuard) { if w.device != nil { w.device.Close() diff --git a/go.mod b/go.mod index d6479643..180b0307 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 github.com/metacubex/sing-shadowsocks v0.2.2-0.20230409073201-1ce3505114ae github.com/metacubex/sing-tun v0.1.3 - github.com/metacubex/sing-wireguard v0.0.0-20230402083957-d134f603ac98 + github.com/metacubex/sing-wireguard v0.0.0-20230413082948-e51777dcf025 github.com/miekg/dns v1.1.52 github.com/mroth/weightedrand/v2 v2.0.0 github.com/openacid/low v0.1.21 diff --git a/go.sum b/go.sum index 7b4d8379..8d4f564e 100644 --- a/go.sum +++ b/go.sum @@ -100,8 +100,8 @@ github.com/metacubex/sing-shadowsocks v0.2.2-0.20230409073201-1ce3505114ae h1:SN github.com/metacubex/sing-shadowsocks v0.2.2-0.20230409073201-1ce3505114ae/go.mod h1:JefDbbkQHCn8w+IcjFqMOMV2VmN9wvbYX4RaeBUkPwM= github.com/metacubex/sing-tun v0.1.3 h1:LCz8TlAZUWwnBYZxs1PEE04PyfLA9xdsye6CjHZlZGQ= github.com/metacubex/sing-tun v0.1.3/go.mod h1:kHkYHoRlYA4I12QPTt/ADyRGqy5YweLUfye1E1hC/q4= -github.com/metacubex/sing-wireguard v0.0.0-20230402083957-d134f603ac98 h1:Dr6A4drhVkjPaJ5lIf3DwhH2HSo+Qmf8fCQmJoDG5YU= -github.com/metacubex/sing-wireguard v0.0.0-20230402083957-d134f603ac98/go.mod h1:7mPG9qYln+CLKBcDt7Dk4c7b3S53VzEfexMVPe6T6FM= +github.com/metacubex/sing-wireguard v0.0.0-20230413082948-e51777dcf025 h1:itjCqEeem+BKZFiBEhQmWHDHKK4FgTGvnYCFS0JOb3A= +github.com/metacubex/sing-wireguard v0.0.0-20230413082948-e51777dcf025/go.mod h1:7mPG9qYln+CLKBcDt7Dk4c7b3S53VzEfexMVPe6T6FM= github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370 h1:UkViS4DCESAUEYgbIEQdD02hyMacFt6Dny+1MOJtNIo= github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= github.com/miekg/dns v1.1.52 h1:Bmlc/qsNNULOe6bpXcUTsuOajd0DzRHwup6D9k1An0c= From b3794ffdd8a401faec927ca307725fd4eac75060 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 13 Apr 2023 19:37:33 +0800 Subject: [PATCH 55/88] fix: deadline pipeReadBuffer, pipeReadFrom and panic when alloc empty buffer --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 180b0307..18d3fb3e 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/openacid/low v0.1.21 github.com/oschwald/geoip2-golang v1.8.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 - github.com/sagernet/sing v0.2.3-0.20230409094616-7f8eaee1b6c8 + github.com/sagernet/sing v0.2.3-0.20230413112320-59e662e6e2ed github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661 github.com/sagernet/sing-vmess v0.1.4-0.20230409073451-6921c3dd77c7 github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 diff --git a/go.sum b/go.sum index 8d4f564e..aea0ce98 100644 --- a/go.sum +++ b/go.sum @@ -144,8 +144,8 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= -github.com/sagernet/sing v0.2.3-0.20230409094616-7f8eaee1b6c8 h1:BWQjek8tNzDzCeHh/8yvjfZ8Id0tl+6pJ+gcPI8tjl8= -github.com/sagernet/sing v0.2.3-0.20230409094616-7f8eaee1b6c8/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= +github.com/sagernet/sing v0.2.3-0.20230413112320-59e662e6e2ed h1:7Vg+lQanPwjxdYaln/EpKK7OcClvoUNm7Tt9V0FPnpY= +github.com/sagernet/sing v0.2.3-0.20230413112320-59e662e6e2ed/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661 h1:QnV79JbJbJGT0MJJfd8o7QMEfRu3eUVKsmahxFMonrc= github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661/go.mod h1:xCeSRP8cV32aPsY+6BbRdJjyD6q8ufdKwhgqxEbU/3U= github.com/sagernet/sing-vmess v0.1.4-0.20230409073451-6921c3dd77c7 h1:ZArINfN+zcHMdZCeRFOm4rO3SWyvYuLg3VhWcA5zonc= From e4926c8364b6f5f25e33e306b417755cbbbbe847 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 14 Apr 2023 13:51:26 +0800 Subject: [PATCH 56/88] feat: ruleset support text `format` --- constant/provider/interface.go | 44 ++++++++++++------ rules/provider/parse.go | 16 ++++++- rules/provider/provider.go | 82 +++++++++++++++++++++------------- 3 files changed, 97 insertions(+), 45 deletions(-) diff --git a/constant/provider/interface.go b/constant/provider/interface.go index b42bbe71..fb5efd87 100644 --- a/constant/provider/interface.go +++ b/constant/provider/interface.go @@ -73,17 +73,27 @@ type ProxyProvider interface { Version() uint32 } -// Rule Type +// RuleProvider interface +type RuleProvider interface { + Provider + Behavior() RuleBehavior + Match(*constant.Metadata) bool + ShouldResolveIP() bool + ShouldFindProcess() bool + AsRule(adaptor string) constant.Rule +} + +// Rule Behavior const ( - Domain RuleType = iota + Domain RuleBehavior = iota IPCIDR Classical ) -// RuleType defined -type RuleType int +// RuleBehavior defined +type RuleBehavior int -func (rt RuleType) String() string { +func (rt RuleBehavior) String() string { switch rt { case Domain: return "Domain" @@ -96,12 +106,20 @@ func (rt RuleType) String() string { } } -// RuleProvider interface -type RuleProvider interface { - Provider - Behavior() RuleType - Match(*constant.Metadata) bool - ShouldResolveIP() bool - ShouldFindProcess() bool - AsRule(adaptor string) constant.Rule +const ( + YamlRule RuleFormat = iota + TextRule +) + +type RuleFormat int + +func (rf RuleFormat) String() string { + switch rf { + case YamlRule: + return "YamlRule" + case TextRule: + return "TextRule" + default: + return "Unknown" + } } diff --git a/rules/provider/parse.go b/rules/provider/parse.go index 206bef10..6a001b50 100644 --- a/rules/provider/parse.go +++ b/rules/provider/parse.go @@ -14,6 +14,7 @@ type ruleProviderSchema struct { Behavior string `provider:"behavior"` Path string `provider:"path"` URL string `provider:"url,omitempty"` + Format string `provider:"format,omitempty"` Interval int `provider:"interval,omitempty"` } @@ -23,7 +24,7 @@ func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(t if err := decoder.Decode(mapping, schema); err != nil { return nil, err } - var behavior P.RuleType + var behavior P.RuleBehavior switch schema.Behavior { case "domain": @@ -36,6 +37,17 @@ func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(t return nil, fmt.Errorf("unsupported behavior type: %s", schema.Behavior) } + var format P.RuleFormat + + switch schema.Format { + case "", "yaml": + format = P.YamlRule + case "text": + format = P.TextRule + default: + return nil, fmt.Errorf("unsupported format type: %s", schema.Format) + } + path := C.Path.Resolve(schema.Path) var vehicle P.Vehicle switch schema.Type { @@ -47,5 +59,5 @@ func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(t return nil, fmt.Errorf("unsupported vehicle type: %s", schema.Type) } - return NewRuleSetProvider(name, behavior, time.Duration(uint(schema.Interval))*time.Second, vehicle, parse), nil + return NewRuleSetProvider(name, behavior, format, time.Duration(uint(schema.Interval))*time.Second, vehicle, parse), nil } diff --git a/rules/provider/provider.go b/rules/provider/provider.go index 6b3c0290..a4271655 100644 --- a/rules/provider/provider.go +++ b/rules/provider/provider.go @@ -6,6 +6,7 @@ import ( "errors" "gopkg.in/yaml.v3" "runtime" + "strings" "time" "github.com/Dreamacro/clash/common/pool" @@ -20,7 +21,8 @@ var ( type ruleSetProvider struct { *resource.Fetcher[any] - behavior P.RuleType + behavior P.RuleBehavior + format P.RuleFormat strategy ruleStrategy } @@ -81,7 +83,7 @@ func (rp *ruleSetProvider) Update() error { return err } -func (rp *ruleSetProvider) Behavior() P.RuleType { +func (rp *ruleSetProvider) Behavior() P.RuleBehavior { return rp.behavior } @@ -105,6 +107,7 @@ func (rp *ruleSetProvider) MarshalJSON() ([]byte, error) { return json.Marshal( map[string]interface{}{ "behavior": rp.behavior.String(), + "format": rp.format.String(), "name": rp.Name(), "ruleCount": rp.strategy.Count(), "type": rp.Type().String(), @@ -113,10 +116,11 @@ func (rp *ruleSetProvider) MarshalJSON() ([]byte, error) { }) } -func NewRuleSetProvider(name string, behavior P.RuleType, interval time.Duration, vehicle P.Vehicle, +func NewRuleSetProvider(name string, behavior P.RuleBehavior, format P.RuleFormat, interval time.Duration, vehicle P.Vehicle, parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) P.RuleProvider { rp := &ruleSetProvider{ behavior: behavior, + format: format, } onUpdate := func(elm interface{}) { @@ -125,7 +129,7 @@ func NewRuleSetProvider(name string, behavior P.RuleType, interval time.Duration } rp.strategy = newStrategy(behavior, parse) - rp.Fetcher = resource.NewFetcher(name, interval, vehicle, func(bytes []byte) (any, error) { return rulesParse(bytes, newStrategy(behavior, parse)) }, onUpdate) + rp.Fetcher = resource.NewFetcher(name, interval, vehicle, func(bytes []byte) (any, error) { return rulesParse(bytes, newStrategy(behavior, parse), format) }, onUpdate) wrapper := &RuleSetProvider{ rp, @@ -136,7 +140,7 @@ func NewRuleSetProvider(name string, behavior P.RuleType, interval time.Duration return wrapper } -func newStrategy(behavior P.RuleType, parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) ruleStrategy { +func newStrategy(behavior P.RuleBehavior, parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) ruleStrategy { switch behavior { case P.Domain: strategy := NewDomainStrategy() @@ -154,7 +158,7 @@ func newStrategy(behavior P.RuleType, parse func(tp, payload, target string, par var ErrNoPayload = errors.New("file must have a `payload` field") -func rulesParse(buf []byte, strategy ruleStrategy) (any, error) { +func rulesParse(buf []byte, strategy ruleStrategy, format P.RuleFormat) (any, error) { strategy.Reset() schema := &RulePayload{} @@ -177,36 +181,54 @@ func rulesParse(buf []byte, strategy ruleStrategy) (any, error) { return nil, ErrNoPayload } } - firstLineBuffer.Write(line) - if firstLineLength == 0 { // find payload head - firstLineLength = firstLineBuffer.Len() - firstLineBuffer.WriteString(" - ''") // a test line + var str string + switch format { + case P.TextRule: + firstLineLength = -1 // don't return ErrNoPayload when read last line + str = string(line) + str = strings.TrimSpace(str) + if str[0] == '#' { // comment + continue + } + if strings.HasPrefix(str, "//") { // comment in Premium core + continue + } + case P.YamlRule: + if bytes.TrimSpace(line)[0] == '#' { // comment + continue + } + firstLineBuffer.Write(line) + if firstLineLength == 0 { // find payload head + firstLineLength = firstLineBuffer.Len() + firstLineBuffer.WriteString(" - ''") // a test line - err := yaml.Unmarshal(firstLineBuffer.Bytes(), schema) - firstLineBuffer.Truncate(firstLineLength) - if err == nil && (len(schema.Rules) > 0 || len(schema.Payload) > 0) { // found + err := yaml.Unmarshal(firstLineBuffer.Bytes(), schema) + firstLineBuffer.Truncate(firstLineLength) + if err == nil && (len(schema.Rules) > 0 || len(schema.Payload) > 0) { // found + continue + } + + // not found or err!=nil + firstLineBuffer.Truncate(0) + firstLineLength = 0 continue } - // not found or err!=nil - firstLineBuffer.Truncate(0) - firstLineLength = 0 - continue + // parse payload body + err := yaml.Unmarshal(firstLineBuffer.Bytes(), schema) + firstLineBuffer.Truncate(firstLineLength) + if err != nil { + continue + } + + if len(schema.Rules) > 0 { + str = schema.Rules[0] + } + if len(schema.Payload) > 0 { + str = schema.Payload[0] + } } - // parse payload body - err := yaml.Unmarshal(firstLineBuffer.Bytes(), schema) - firstLineBuffer.Truncate(firstLineLength) - if err != nil { - continue - } - var str string - if len(schema.Rules) > 0 { - str = schema.Rules[0] - } - if len(schema.Payload) > 0 { - str = schema.Payload[0] - } if str == "" { continue } From 81bef30ae0a152484db3aa3d5cbc75388c3f60b4 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Fri, 14 Apr 2023 10:04:39 +0000 Subject: [PATCH 57/88] chore: clean up docs --- docs/allocs.svg | 2387 ----------------------------------------------- docs/heap.svg | 2182 ------------------------------------------- 2 files changed, 4569 deletions(-) delete mode 100644 docs/allocs.svg delete mode 100644 docs/heap.svg diff --git a/docs/allocs.svg b/docs/allocs.svg deleted file mode 100644 index 57deec50..00000000 --- a/docs/allocs.svg +++ /dev/null @@ -1,2387 +0,0 @@ - - - - - - -unnamed - - -cluster_L - - - - -Type: alloc_space - -Type: alloc_space -Time: Jan 30, 2023 at 9:18pm (CST) -Showing nodes accounting for 1777.16MB, 96.71% of 1837.65MB total -Dropped 188 nodes (cum <= 9.19MB) -Dropped 2 edges (freq <= 1.84MB) -Showing top 55 nodes out of 117 -See https://git.io/JfYMW for how to read the graph - - - -N1 - - -quic-go -(*client) -dial -func1 -0 of 1648.40MB (89.70%) - - - - - -N3 - - -quic-go -(*connection) -run -0 of 1648.40MB (89.70%) - - - - - -N1->N3 - - - - - - - 1648.40MB - - - - - -N2 - - -quic-go -(*connection) -sendPacket -0 of 1412.44MB (76.86%) - - - - - -N4 - - -quic-go -(*packetPacker) -PackPacket -30MB (1.63%) -of 956.14MB (52.03%) - - - - - -N2->N4 - - - - - - - 956.14MB - - - - - -N10 - - -quic-go -(*connection) -sendPackedPacket -0 of 456.30MB (24.83%) - - - - - -N2->N10 - - - - - - - 456.30MB - - - - - -N3->N2 - - - - - - - 1412.44MB - - - - - -N36 - - -quic-go -(*connection) -handleUnpackedShortHeaderPacket -0 of 235.46MB (12.81%) - - - - - -N3->N36 - - - - - - - 235.46MB - - - - - -NN4_0 - - - - - -16B..64B - - - - - -N4->NN4_0 - - - - - - - 30MB - - - - - -N6 - - -quic-go -(*packetPacker) -maybeGetShortHeaderPacket -0 of 722.12MB (39.30%) - - - - - -N4->N6 - - - - - - - 722.12MB - - - - - -N13 - - -quic-go -(*packetPacker) -appendPacket -118.51MB (6.45%) -of 204.01MB (11.10%) - - - - - -N4->N13 - - - - - - - 204.01MB - - - - - -N5 - - -quic-go -(*packetPacker) -composeNextPacket -162.51MB (8.84%) -of 446.09MB (24.28%) - - - - - -NN5_0 - - - - - -48B..64B - - - - - -N5->NN5_0 - - - - - - - 93.50MB - - - - - -NN5_1 - - - - - -16B..32B - - - - - -N5->NN5_1 - - - - - - - 69MB - - - - - -N25 - - -quic-go -(*framerI) -AppendStreamFrames -15.50MB (0.84%) -of 157.55MB (8.57%) - - - - - -N5->N25 - - - - - - - 157.55MB - - - - - -N34 - - -ackhandler -(*receivedPacketTracker) -GetAckFrame -0 of 126.04MB (6.86%) - - - - - -N5->N34 - - - - - - - 126.04MB - - - - - -N6->N5 - - - - - - - 446.09MB - - - - - -N8 - - -quic-go -(*packetPacker) -getShortHeader -276.03MB (15.02%) - - - - - -N6->N8 - - - - - - - 276.03MB - - - - - -N7 - - -sync -(*Pool) -Get -0 of 248.65MB (13.53%) - - - - - -N12 - - -ackhandler -glob -func1 -160.01MB (8.71%) - - - - - -N7->N12 - - - - - - - 160.01MB - - - - - -N19 - - -wire -init -0 -func1 -70.60MB (3.84%) - - - - - -N7->N19 - - - - - - - 70.60MB - - - - - -N44 - - -wire -glob -func1 -15MB (0.82%) - - - - - -N7->N44 - - - - - - - 15MB - - - - - -NN8_0 - - - - - -128B - - - - - -N8->NN8_0 - - - - - - - 276.03MB - - - - - -N9 - - -congestion -NewConnectionStateOnSentPacket -236.03MB (12.84%) - - - - - -NN9_0 - - - - - -128B - - - - - -N9->NN9_0 - - - - - - - 236.03MB - - - - - -N10->N7 - - - - - - - 160.01MB - - - - - -N17 - - -ackhandler -(*sentPacketHandler) -SentPacket -0 of 296.29MB (16.12%) - - - - - -N10->N17 - - - - - - - 296.29MB - - - - - -N11 - - -linkedlist -(*List[…]) -insertValue -120MB (6.53%) - - - - - -NN11_0 - - - - - -32B - - - - - -N11->NN11_0 - - - - - - - 113.50MB - - - - - -NN12_0 - - - - - -96B - - - - - -N12->NN12_0 - - - - - - - 160.01MB - - - - - -NN13_0 - - - - - -64B - - - - - -N13->NN13_0 - - - - - - - 118.51MB - - - - - -N18 - - -bytes -NewBuffer -85.50MB (4.65%) - - - - - -N13->N18 - - - - - - - 85.50MB - (inline) - - - - - -N14 - - -ackhandler -(*receivedPacketHistory) -AppendAckRanges -115.54MB (6.29%) - - - - - -NN14_0 - - - - - -512B - - - - - -N14->NN14_0 - - - - - - - 51.53MB - - - - - -NN14_1 - - - - - -256B - - - - - -N14->NN14_1 - - - - - - - 29.51MB - - - - - -NN14_2 - - - - - -128B - - - - - -N14->NN14_2 - - - - - - - 20MB - - - - - -N15 - - -quic-go -(*connection) -handleFrames -0 of 228.96MB (12.46%) - - - - - -N21 - - -wire -(*frameParser) -parseFrame -0 of 109.58MB (5.96%) - - - - - -N15->N21 - - - - - - - 109.58MB - - - - - -N23 - - -quic-go -(*connection) -handleFrame -0 of 119.38MB (6.50%) - - - - - -N15->N23 - - - - - - - 119.38MB - - - - - -N16 - - -quic-go -(*sendStream) -popStreamFrame -111.50MB (6.07%) -of 142.05MB (7.73%) - - - - - -NN16_0 - - - - - -16B - - - - - -N16->NN16_0 - - - - - - - 57MB - - - - - -NN16_1 - - - - - -32B - - - - - -N16->NN16_1 - - - - - - - 54.50MB - - - - - -N29 - - -wire -GetStreamFrame -0 of 70.60MB (3.84%) - - - - - -N16->N29 - - - - - - - 30.54MB - (inline) - - - - - -N17->N9 - - - - - - - 236.03MB - (inline) - - - - - -N17->N11 - - - - - - - 57.50MB - (inline) - - - - - -NN18_0 - - - - - -48B - - - - - -N18->NN18_0 - - - - - - - 85.50MB - - - - - -NN19_0 - - - - - -1.50kB - - - - - -N19->NN19_0 - - - - - - - 70.60MB - - - - - -N20 - - -geodata -LoadGeoSiteMatcher -0 of 74.20MB (4.04%) - - - - - -N33 - - -router -NewMphMatcherGroup -0 of 57.15MB (3.11%) - - - - - -N20->N33 - - - - - - - 57.15MB - - - - - -N40 - - -reflect -New -13MB (0.71%) - - - - - -N20->N40 - - - - - - - 11.50MB - - - - - -N24 - - -wire -parseAckFrame -65.02MB (3.54%) -of 69.52MB (3.78%) - - - - - -N21->N24 - - - - - - - 69.52MB - - - - - -N21->N29 - - - - - - - 40.06MB - (inline) - - - - - -N22 - - -quic-go -(*basicConn) -ReadPacket -35.50MB (1.93%) -of 59.01MB (3.21%) - - - - - -N22->N7 - - - - - - - 2MB - - - - - -NN22_0 - - - - - -96B - - - - - -N22->NN22_0 - - - - - - - 35.50MB - - - - - -N37 - - -net -(*UDPConn) -ReadFrom -18.50MB (1.01%) -of 22MB (1.20%) - - - - - -N22->N37 - - - - - - - 21.50MB - - - - - -N41 - - -ackhandler -(*sentPacketHandler) -ReceivedAck -0 of 90.10MB (4.90%) - - - - - -N23->N41 - - - - - - - 90.10MB - - - - - -N42 - - -quic-go -(*receiveStream) -handleStreamFrameImpl -7MB (0.38%) -of 29.28MB (1.59%) - - - - - -N23->N42 - - - - - - - 29.28MB - - - - - -N24->N7 - - - - - - - 4.50MB - - - - - -NN24_0 - - - - - -512B - - - - - -N24->NN24_0 - - - - - - - 32.52MB - - - - - -NN24_1 - - - - - -256B - - - - - -N24->NN24_1 - - - - - - - 18MB - - - - - -N25->N16 - - - - - - - 142.05MB - - - - - -NN25_0 - - - - - -16B - - - - - -N25->NN25_0 - - - - - - - 15.50MB - - - - - -N26 - - -runtime -main -0 of 79.85MB (4.35%) - - - - - -N52 - - -main -main -0 of 78.85MB (4.29%) - - - - - -N26->N52 - - - - - - - 78.85MB - - - - - -N27 - - -strmatcher -(*MphMatcherGroup) -Build -37.85MB (2.06%) - - - - - -N28 - - -ackhandler -(*sentPacketHandler) -detectLostPackets -func1 -0 of 79.59MB (4.33%) - - - - - -N35 - - -quic-go -(*sendStream) -queueRetransmission -23.59MB (1.28%) - - - - - -N28->N35 - - - - - - - 23.59MB - - - - - -N43 - - -linkedlist -(*List[…]) -InsertAfter -0 of 62.50MB (3.40%) - - - - - -N28->N43 - - - - - - - 56MB - - - - - -N29->N7 - - - - - - - 70.60MB - - - - - -N30 - - -common -NewGEOSITE -0 of 74.20MB (4.04%) - - - - - -N30->N20 - - - - - - - 25.91MB - - - - - -N46 - - -geodata -Verify -0 of 51.44MB (2.80%) - - - - - -N30->N46 - - - - - - - 48.29MB - - - - - -N31 - - -quic-go -(*packetHandlerMap) -listen -0 of 59.01MB (3.21%) - - - - - -N31->N22 - - - - - - - 59.01MB - - - - - -N32 - - -http -HandlerFunc -ServeHTTP -0 of 26.60MB (1.45%) - - - - - -N47 - - -chi -(*Mux) -routeHTTP -0 of 26.60MB (1.45%) - - - - - -N32->N47 - - - - - - - 26.10MB - - - - - -N48 - - -chi -(*Mux) -ServeHTTP -0 of 26.10MB (1.42%) - - - - - -N32->N48 - - - - - - - 25.59MB - - - - - -N33->N27 - - - - - - - 37.85MB - - - - - -N38 - - -strmatcher -(*MphMatcherGroup) -AddFullOrDomainPattern -18.80MB (1.02%) - - - - - -N33->N38 - - - - - - - 18.80MB - (inline) - - - - - -N34->N7 - - - - - - - 10.50MB - - - - - -N34->N14 - - - - - - - 115.54MB - - - - - -N36->N15 - - - - - - - 228.96MB - - - - - -NN37_0 - - - - - -48B - - - - - -N37->NN37_0 - - - - - - - 18.50MB - - - - - -N39 - - -sync -(*poolChain) -pushHead -14.30MB (0.78%) - - - - - -NN40_0 - - - - - -96B - - - - - -N40->NN40_0 - - - - - - - 11.50MB - - - - - -N41->N28 - - - - - - - 79.59MB - - - - - -N41->N39 - - - - - - - 5.51MB - - - - - -NN42_0 - - - - - -16B - - - - - -N42->NN42_0 - - - - - - - 7MB - - - - - -N49 - - -quic-go -(*frameSorter) -push -5.28MB (0.29%) -of 22.28MB (1.21%) - - - - - -N42->N49 - - - - - - - 22.28MB - - - - - -N43->N11 - - - - - - - 62.50MB - (inline) - - - - - -NN44_0 - - - - - -64B - - - - - -N44->NN44_0 - - - - - - - 15MB - - - - - -N45 - - -flate -NewWriter -10.58MB (0.58%) -of 17.16MB (0.93%) - - - - - -NN45_0 - - - - - -648kB - - - - - -N45->NN45_0 - - - - - - - 10.58MB - - - - - -N46->N20 - - - - - - - 48.29MB - - - - - -N47->N32 - - - - - - - 26.60MB - - - - - -N53 - - -pprof -(*Profile) -WriteTo -0 of 22.36MB (1.22%) - - - - - -N47->N53 - - - - - - - 22.36MB - - - - - -N48->N32 - - - - - - - 26.10MB - - - - - -N51 - - -tree -insert[…] -11MB (0.6%) - - - - - -N49->N51 - - - - - - - 11MB - - - - - -N50 - - -http -(*conn) -serve -0 of 24.10MB (1.31%) - - - - - -N50->N48 - - - - - - - 24.10MB - - - - - -NN51_0 - - - - - -48B - - - - - -N51->NN51_0 - - - - - - - 11MB - - - - - -N52->N30 - - - - - - - 74.20MB - - - - - -N54 - - -pprof -writeHeapInternal -0 of 22.36MB (1.22%) - - - - - -N53->N54 - - - - - - - 22.36MB - - - - - -N55 - - -pprof -writeHeapProto -0 of 22.36MB (1.22%) - - - - - -N54->N55 - - - - - - - 22.36MB - - - - - -N55->N45 - - - - - - - 16.28MB - - - - - diff --git a/docs/heap.svg b/docs/heap.svg deleted file mode 100644 index 0795977f..00000000 --- a/docs/heap.svg +++ /dev/null @@ -1,2182 +0,0 @@ - - - - - - -unnamed - - -cluster_L - - - - -Type: inuse_space - -Type: inuse_space -Time: Jan 30, 2023 at 9:18pm (CST) -Showing nodes accounting for 12146.04kB, 100% of 12146.04kB total -Showing top 69 nodes out of 71 -See https://git.io/JfYMW for how to read the graph - - - -N1 - - -runtime -allocm -5637.50kB (46.41%) - - - - - -NN1_0 - - - - - -1kB - - - - - -N1->NN1_0 - - - - - - - 5637.50kB - - - - - -N2 - - -runtime -schedule -0 of 5637.50kB (46.41%) - - - - - -N68 - - -runtime -resetspinning -0 of 5637.50kB (46.41%) - - - - - -N2->N68 - - - - - - - 5637.50kB - - - - - -N3 - - -runtime -mstart -0 of 3587.50kB (29.54%) - - - - - -N62 - - -runtime -mstart0 -0 of 3587.50kB (29.54%) - - - - - -N3->N62 - - - - - - - 3587.50kB - - - - - -N4 - - -runtime -main -0 of 2836.92kB (23.36%) - - - - - -N61 - - -main -main -0 of 2836.92kB (23.36%) - - - - - -N4->N61 - - - - - - - 2836.92kB - - - - - -N5 - - -geodata -LoadGeoSiteMatcher -0 of 2836.92kB (23.36%) - - - - - -N10 - - -router -NewMphMatcherGroup -0 of 1812.91kB (14.93%) - - - - - -N5->N10 - - - - - - - 1812.91kB - - - - - -N27 - - -geodata -(*loader) -LoadGeoSite -0 of 1024.02kB (8.43%) - - - - - -N5->N27 - - - - - - - 1024.02kB - - - - - -N6 - - -strmatcher -(*MphMatcherGroup) -Build -1300.89kB (10.71%) - - - - - -NN6_0 - - - - - -1.14MB - - - - - -N6->NN6_0 - - - - - - - 1300.89kB - - - - - -N7 - - -runtime -mcall -0 of 2050kB (16.88%) - - - - - -N67 - - -runtime -park_m -0 of 2050kB (16.88%) - - - - - -N7->N67 - - - - - - - 2050kB - - - - - -N8 - - -impl -(*MessageInfo) -unmarshalPointer -0 of 1024.02kB (8.43%) - - - - - -N9 - - -impl -consumeStringValidateUTF8 -1024.02kB (8.43%) - - - - - -N8->N9 - - - - - - - 1024.02kB - - - - - -N58 - - -impl -consumeMessageSliceInfo -0 of 1024.02kB (8.43%) - - - - - -N8->N58 - - - - - - - 1024.02kB - - - - - -NN9_0 - - - - - -16B - - - - - -N9->NN9_0 - - - - - - - 1024.02kB - - - - - -N10->N6 - - - - - - - 1300.89kB - - - - - -N31 - - -strmatcher -(*MphMatcherGroup) -AddPattern -0 of 512.01kB (4.22%) - - - - - -N10->N31 - - - - - - - 512.01kB - - - - - -N11 - - -runtime -gcBgMarkWorker -512.02kB (4.22%) - - - - - -NN11_0 - - - - - -32B - - - - - -N11->NN11_0 - - - - - - - 512.02kB - - - - - -N12 - - -congestion -(*ConnectionStates) -Insert -596.16kB (4.91%) - - - - - -NN12_0 - - - - - -160kB - - - - - -N12->NN12_0 - - - - - - - 596.16kB - - - - - -N13 - - -bufio -NewReaderSize -514kB (4.23%) - - - - - -NN13_0 - - - - - -4kB - - - - - -N13->NN13_0 - - - - - - - 514kB - - - - - -N14 - - -quic-go -init -0 -func1 -512.75kB (4.22%) - - - - - -NN14_0 - - - - - -1.50kB - - - - - -N14->NN14_0 - - - - - - - 512.75kB - - - - - -N15 - - -trie -(*Node[…]) -optimize -512.44kB (4.22%) - - - - - -NN15_0 - - - - - -896B - - - - - -N15->NN15_0 - - - - - - - 512.44kB - - - - - -N16 - - -runtime -malg -512.20kB (4.22%) - - - - - -NN16_0 - - - - - -416B - - - - - -N16->NN16_0 - - - - - - - 512.20kB - - - - - -N17 - - -time -NewTicker -512.05kB (4.22%) - - - - - -NN17_0 - - - - - -96B - - - - - -N17->NN17_0 - - - - - - - 512.05kB - - - - - -N18 - - -strmatcher -(*MphMatcherGroup) -AddFullOrDomainPattern -512.01kB (4.22%) - - - - - -NN18_0 - - - - - -24B - - - - - -N18->NN18_0 - - - - - - - 512.01kB - - - - - -N19 - - -quic-go -(*client) -dial -func1 -0 of 596.16kB (4.91%) - - - - - -N49 - - -quic-go -(*connection) -run -0 of 596.16kB (4.91%) - - - - - -N19->N49 - - - - - - - 596.16kB - - - - - -N20 - - -mixed -handleConn -0 of 514kB (4.23%) - - - - - -N26 - - -net -NewBufferedConn -0 of 514kB (4.23%) - - - - - -N20->N26 - - - - - - - 514kB - - - - - -N21 - - -quic-go -(*packetHandlerMap) -listen -0 of 512.75kB (4.22%) - - - - - -N48 - - -quic-go -(*basicConn) -ReadPacket -0 of 512.75kB (4.22%) - - - - - -N21->N48 - - - - - - - 512.75kB - - - - - -N22 - - -executor -loadRuleProvider -func1 -0 of 512.44kB (4.22%) - - - - - -N40 - - -executor -loadProvider -0 of 512.44kB (4.22%) - - - - - -N22->N40 - - - - - - - 512.44kB - - - - - -N23 - - -runtime -systemstack -0 of 512.20kB (4.22%) - - - - - -N65 - - -runtime -newproc -func1 -0 of 512.20kB (4.22%) - - - - - -N23->N65 - - - - - - - 512.20kB - - - - - -N24 - - -statistic -(*Manager) -handle -0 of 512.05kB (4.22%) - - - - - -N24->N17 - - - - - - - 512.05kB - - - - - -N25 - - -bufio -NewReader -0 of 514kB (4.23%) - - - - - -N25->N13 - - - - - - - 514kB - (inline) - - - - - -N26->N25 - - - - - - - 514kB - (inline) - - - - - -N28 - - -geodata -(*loader) -LoadGeoSiteWithAttr -0 of 1024.02kB (8.43%) - - - - - -N27->N28 - - - - - - - 1024.02kB - - - - - -N29 - - -memconservative -(*memConservativeLoader) -LoadSiteByPath -0 of 1024.02kB (8.43%) - - - - - -N28->N29 - - - - - - - 1024.02kB - - - - - -N30 - - -memconservative -GeoSiteCache -Unmarshal -0 of 1024.02kB (8.43%) - - - - - -N29->N30 - - - - - - - 1024.02kB - - - - - -N59 - - -proto -Unmarshal -0 of 1024.02kB (8.43%) - - - - - -N30->N59 - - - - - - - 1024.02kB - - - - - -N31->N18 - - - - - - - 512.01kB - (inline) - - - - - -N32 - - -trie -(*DomainTrie[…]) -Optimize -0 of 512.44kB (4.22%) - - - - - -N32->N15 - - - - - - - 512.44kB - - - - - -N33 - - -config -Parse -0 of 2836.92kB (23.36%) - - - - - -N34 - - -config -ParseRawConfig -0 of 2836.92kB (23.36%) - - - - - -N33->N34 - - - - - - - 2836.92kB - - - - - -N35 - - -config -parseRules -0 of 2836.92kB (23.36%) - - - - - -N34->N35 - - - - - - - 2836.92kB - - - - - -N41 - - -rules -ParseRule -0 of 2836.92kB (23.36%) - - - - - -N35->N41 - - - - - - - 2836.92kB - - - - - -N36 - - -hub -Parse -0 of 2836.92kB (23.36%) - - - - - -N37 - - -executor -Parse -0 of 2836.92kB (23.36%) - - - - - -N36->N37 - - - - - - - 2836.92kB - (inline) - - - - - -N39 - - -executor -ParseWithPath -0 of 2836.92kB (23.36%) - - - - - -N37->N39 - - - - - - - 2836.92kB - - - - - -N38 - - -executor -ParseWithBytes -0 of 2836.92kB (23.36%) - - - - - -N38->N33 - - - - - - - 2836.92kB - - - - - -N39->N38 - - - - - - - 2836.92kB - (inline) - - - - - -N44 - - -provider -(*ruleSetProvider) -Initial -0 of 512.44kB (4.22%) - - - - - -N40->N44 - - - - - - - 512.44kB - - - - - -N42 - - -common -NewGEOSITE -0 of 2836.92kB (23.36%) - - - - - -N41->N42 - - - - - - - 2836.92kB - - - - - -N42->N5 - - - - - - - 2836.92kB - - - - - -N43 - - -provider -(*domainStrategy) -OnUpdate -0 of 512.44kB (4.22%) - - - - - -N43->N32 - - - - - - - 512.44kB - - - - - -N45 - - -provider -NewRuleSetProvider -func1 -0 of 512.44kB (4.22%) - - - - - -N44->N45 - - - - - - - 512.44kB - - - - - -N45->N43 - - - - - - - 512.44kB - - - - - -N46 - - -congestion -(*BandwidthSampler) -OnPacketSent -0 of 596.16kB (4.91%) - - - - - -N46->N12 - - - - - - - 596.16kB - (inline) - - - - - -N47 - - -congestion -(*bbrSender) -OnPacketSent -0 of 596.16kB (4.91%) - - - - - -N47->N46 - - - - - - - 596.16kB - - - - - -N53 - - -quic-go -getPacketBuffer -0 of 512.75kB (4.22%) - - - - - -N48->N53 - - - - - - - 512.75kB - (inline) - - - - - -N52 - - -quic-go -(*connection) -sendPackets -0 of 596.16kB (4.91%) - - - - - -N49->N52 - - - - - - - 596.16kB - - - - - -N50 - - -quic-go -(*connection) -sendPackedPacket -0 of 596.16kB (4.91%) - - - - - -N55 - - -ackhandler -(*sentPacketHandler) -SentPacket -0 of 596.16kB (4.91%) - - - - - -N50->N55 - - - - - - - 596.16kB - - - - - -N51 - - -quic-go -(*connection) -sendPacket -0 of 596.16kB (4.91%) - - - - - -N51->N50 - - - - - - - 596.16kB - - - - - -N52->N51 - - - - - - - 596.16kB - - - - - -N53->N14 - - - - - - - 512.75kB - - - - - -N54 - - -ackhandler -(*ccAdapter) -OnPacketSent -0 of 596.16kB (4.91%) - - - - - -N54->N47 - - - - - - - 596.16kB - - - - - -N56 - - -ackhandler -(*sentPacketHandler) -sentPacketImpl -0 of 596.16kB (4.91%) - - - - - -N55->N56 - - - - - - - 596.16kB - - - - - -N56->N54 - - - - - - - 596.16kB - - - - - -N57 - - -impl -(*MessageInfo) -unmarshal -0 of 1024.02kB (8.43%) - - - - - -N57->N8 - - - - - - - 1024.02kB - - - - - -N58->N8 - - - - - - - 1024.02kB - - - - - -N60 - - -proto -UnmarshalOptions -unmarshal -0 of 1024.02kB (8.43%) - - - - - -N59->N60 - - - - - - - 1024.02kB - - - - - -N60->N57 - - - - - - - 1024.02kB - - - - - -N61->N36 - - - - - - - 2836.92kB - - - - - -N63 - - -runtime -mstart1 -0 of 3587.50kB (29.54%) - - - - - -N62->N63 - - - - - - - 3587.50kB - - - - - -N63->N2 - - - - - - - 3587.50kB - - - - - -N64 - - -runtime -newm -0 of 5637.50kB (46.41%) - - - - - -N64->N1 - - - - - - - 5637.50kB - - - - - -N66 - - -runtime -newproc1 -0 of 512.20kB (4.22%) - - - - - -N65->N66 - - - - - - - 512.20kB - - - - - -N66->N16 - - - - - - - 512.20kB - - - - - -N67->N2 - - - - - - - 2050kB - - - - - -N69 - - -runtime -startm -0 of 5637.50kB (46.41%) - - - - - -N68->N69 - - - - - - - 5637.50kB - - - - - -N69->N64 - - - - - - - 5637.50kB - - - - - From c2d1f713059f8483ed4ff76242d666bbc61123b3 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 14 Apr 2023 19:06:25 +0800 Subject: [PATCH 58/88] fix: ruleProvider panic --- rules/provider/provider.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/rules/provider/provider.go b/rules/provider/provider.go index a4271655..65d21d2f 100644 --- a/rules/provider/provider.go +++ b/rules/provider/provider.go @@ -187,6 +187,9 @@ func rulesParse(buf []byte, strategy ruleStrategy, format P.RuleFormat) (any, er firstLineLength = -1 // don't return ErrNoPayload when read last line str = string(line) str = strings.TrimSpace(str) + if len(str) == 0 { + continue + } if str[0] == '#' { // comment continue } @@ -194,7 +197,11 @@ func rulesParse(buf []byte, strategy ruleStrategy, format P.RuleFormat) (any, er continue } case P.YamlRule: - if bytes.TrimSpace(line)[0] == '#' { // comment + trimLine := bytes.TrimSpace(line) + if len(trimLine) == 0 { + continue + } + if trimLine[0] == '#' { // comment continue } firstLineBuffer.Write(line) From 8e5dbc738291e96e87ad42705a80776fe43c6500 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 15 Apr 2023 16:56:43 +0800 Subject: [PATCH 59/88] chore: Update dependencies --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 18d3fb3e..971d4291 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 - github.com/metacubex/sing-shadowsocks v0.2.2-0.20230409073201-1ce3505114ae + github.com/metacubex/sing-shadowsocks v0.2.2-0.20230414151724-274b69df9875 github.com/metacubex/sing-tun v0.1.3 github.com/metacubex/sing-wireguard v0.0.0-20230413082948-e51777dcf025 github.com/miekg/dns v1.1.52 @@ -28,9 +28,9 @@ require ( github.com/openacid/low v0.1.21 github.com/oschwald/geoip2-golang v1.8.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 - github.com/sagernet/sing v0.2.3-0.20230413112320-59e662e6e2ed - github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661 - github.com/sagernet/sing-vmess v0.1.4-0.20230409073451-6921c3dd77c7 + github.com/sagernet/sing v0.2.3 + github.com/sagernet/sing-shadowtls v0.1.1 + github.com/sagernet/sing-vmess v0.1.4 github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c diff --git a/go.sum b/go.sum index aea0ce98..c45880a9 100644 --- a/go.sum +++ b/go.sum @@ -96,8 +96,8 @@ github.com/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03 h1:gREIdurac9fpyB github.com/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03/go.mod h1:wqEuzdImyqD2MCGE8CYRJXbB77oSEJeoSSXXdwKjnsE= github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 h1:KD96JPdTIayTGGgRl6PuVqo2Bpo6+x3LqDDyqrYDDXw= github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594/go.mod h1:9nOiGX6kqV3+ZbkDKdTNzdFD726QQHPH6WDb36jUSpA= -github.com/metacubex/sing-shadowsocks v0.2.2-0.20230409073201-1ce3505114ae h1:SNnvqnJrqVErz1Qoo3thxa4lgdTPgBSQpJKQJcQjz9Y= -github.com/metacubex/sing-shadowsocks v0.2.2-0.20230409073201-1ce3505114ae/go.mod h1:JefDbbkQHCn8w+IcjFqMOMV2VmN9wvbYX4RaeBUkPwM= +github.com/metacubex/sing-shadowsocks v0.2.2-0.20230414151724-274b69df9875 h1:TrTg8JY//QOu4JZuXjKzB35Gppz4B4Us9EIvtdno6vo= +github.com/metacubex/sing-shadowsocks v0.2.2-0.20230414151724-274b69df9875/go.mod h1:0ebzwmiUVa4wAmjhzaEx5BgeuhJFT5QYO0oxwolKlYE= github.com/metacubex/sing-tun v0.1.3 h1:LCz8TlAZUWwnBYZxs1PEE04PyfLA9xdsye6CjHZlZGQ= github.com/metacubex/sing-tun v0.1.3/go.mod h1:kHkYHoRlYA4I12QPTt/ADyRGqy5YweLUfye1E1hC/q4= github.com/metacubex/sing-wireguard v0.0.0-20230413082948-e51777dcf025 h1:itjCqEeem+BKZFiBEhQmWHDHKK4FgTGvnYCFS0JOb3A= @@ -144,12 +144,12 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= -github.com/sagernet/sing v0.2.3-0.20230413112320-59e662e6e2ed h1:7Vg+lQanPwjxdYaln/EpKK7OcClvoUNm7Tt9V0FPnpY= -github.com/sagernet/sing v0.2.3-0.20230413112320-59e662e6e2ed/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= -github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661 h1:QnV79JbJbJGT0MJJfd8o7QMEfRu3eUVKsmahxFMonrc= -github.com/sagernet/sing-shadowtls v0.1.1-0.20230408141548-81d74d2a8661/go.mod h1:xCeSRP8cV32aPsY+6BbRdJjyD6q8ufdKwhgqxEbU/3U= -github.com/sagernet/sing-vmess v0.1.4-0.20230409073451-6921c3dd77c7 h1:ZArINfN+zcHMdZCeRFOm4rO3SWyvYuLg3VhWcA5zonc= -github.com/sagernet/sing-vmess v0.1.4-0.20230409073451-6921c3dd77c7/go.mod h1:A9iGfwYmAEmVg8bavkqQ7Hx7nr9Pc2oWMYDwbLIBDjY= +github.com/sagernet/sing v0.2.3 h1:V50MvZ4c3Iij2lYFWPlzL1PyipwSzjGeN9x+Ox89vpk= +github.com/sagernet/sing v0.2.3/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= +github.com/sagernet/sing-shadowtls v0.1.1 h1:Gf8YD/4zCjfM9xxVzI0QVYAnjMCtsDHcIp84CUdY3VA= +github.com/sagernet/sing-shadowtls v0.1.1/go.mod h1:espnHDPRQeG95ZoU3xfHDFnc6Nt4GvbbuPV2okN+x/E= +github.com/sagernet/sing-vmess v0.1.4 h1:aZ03KgmR9COPitVqo8NTPHeaHmbJYDZ8mkumnUV4oGQ= +github.com/sagernet/sing-vmess v0.1.4/go.mod h1:NensKZJDgfPsUf3TzFD8kTuzI3CQfN1Tj3Eygti5Isg= github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE= github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9/go.mod h1:FUyTEc5ye5NjKnDTDMuiLF2M6T4BE6y6KZuax//UCEg= github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfIATJ8oQwBmpOZJuozQG7Vk88lL4= From 4a0d097fe9f1b8fe352267040658331168e8abd8 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 17 Apr 2023 00:23:12 +0800 Subject: [PATCH 60/88] fix: ensure StreamWebsocketConn call N.NewDeadlineConn --- common/net/bufconn.go | 4 ++++ transport/vmess/websocket.go | 14 +++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/common/net/bufconn.go b/common/net/bufconn.go index 8087608c..f01a3dda 100644 --- a/common/net/bufconn.go +++ b/common/net/bufconn.go @@ -79,3 +79,7 @@ func (c *BufferedConn) ReaderReplaceable() bool { } return true } + +func (c *BufferedConn) WriterReplaceable() bool { + return true +} diff --git a/transport/vmess/websocket.go b/transport/vmess/websocket.go index 179da886..e7335d84 100644 --- a/transport/vmess/websocket.go +++ b/transport/vmess/websocket.go @@ -334,7 +334,10 @@ func streamWebsocketWithEarlyDataConn(conn net.Conn, c *WebsocketConfig) (net.Co underlay: conn, config: c, } - return conn, nil + // websocketWithEarlyDataConn can't correct handle Deadline + // it will not apply the already set Deadline after Dial() + // so call N.NewDeadlineConn to add a safe wrapper + return N.NewDeadlineConn(conn), nil } func streamWebsocketConn(conn net.Conn, c *WebsocketConfig, earlyData *bytes.Buffer) (net.Conn, error) { @@ -402,11 +405,16 @@ func streamWebsocketConn(conn net.Conn, c *WebsocketConfig, earlyData *bytes.Buf return nil, fmt.Errorf("dial %s error: %s", uri.Host, reason) } - return &websocketConn{ + conn = &websocketConn{ conn: wsConn, rawWriter: N.NewExtendedWriter(wsConn.UnderlyingConn()), remoteAddr: conn.RemoteAddr(), - }, nil + } + // websocketConn can't correct handle ReadDeadline + // gorilla/websocket will cache the os.ErrDeadlineExceeded from conn.Read() + // it will cause read fail and event panic in *websocket.Conn.NextReader() + // so call N.NewDeadlineConn to add a safe wrapper + return N.NewDeadlineConn(conn), nil } func StreamWebsocketConn(conn net.Conn, c *WebsocketConfig) (net.Conn, error) { From 495033270cc1d7ce2f9350ad46eb0363c5ffdaf0 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 17 Apr 2023 19:29:07 +0800 Subject: [PATCH 61/88] chore: using new chan based deadline reader --- adapter/outbound/base.go | 11 +++++++++-- common/net/sing.go | 10 ++-------- component/dialer/tfo.go | 4 ++++ go.mod | 8 ++++---- go.sum | 16 ++++++++-------- listener/shadowsocks/tcp.go | 3 ++- listener/shadowsocks/udp.go | 3 +-- listener/sing/sing.go | 4 ++++ 8 files changed, 34 insertions(+), 25 deletions(-) diff --git a/adapter/outbound/base.go b/adapter/outbound/base.go index 0d6cdb8c..04eec966 100644 --- a/adapter/outbound/base.go +++ b/adapter/outbound/base.go @@ -5,6 +5,7 @@ import ( "encoding/json" "net" "strings" + "syscall" N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/utils" @@ -204,7 +205,10 @@ func (c *conn) Upstream() any { } func NewConn(c net.Conn, a C.ProxyAdapter) C.Conn { - return &conn{N.NewDeadlineConn(c), []string{a.Name()}, parseRemoteDestination(a.Addr())} + if _, ok := c.(syscall.Conn); !ok { // exclusion system conn like *net.TCPConn + c = N.NewDeadlineConn(c) // most conn from outbound can't handle readDeadline correctly + } + return &conn{N.NewExtendedConn(c), []string{a.Name()}, parseRemoteDestination(a.Addr())} } type packetConn struct { @@ -235,7 +239,10 @@ func (c *packetConn) LocalAddr() net.Addr { } func newPacketConn(pc net.PacketConn, a C.ProxyAdapter) C.PacketConn { - return &packetConn{N.NewDeadlinePacketConn(pc), []string{a.Name()}, a.Name(), utils.NewUUIDV4().String(), parseRemoteDestination(a.Addr())} + if _, ok := pc.(syscall.Conn); !ok { // exclusion system conn like *net.UDPConn + pc = N.NewDeadlinePacketConn(pc) // most conn from outbound can't handle readDeadline correctly + } + return &packetConn{pc, []string{a.Name()}, a.Name(), utils.NewUUIDV4().String(), parseRemoteDestination(a.Addr())} } func parseRemoteDestination(addr string) string { diff --git a/common/net/sing.go b/common/net/sing.go index 4aad5523..89517326 100644 --- a/common/net/sing.go +++ b/common/net/sing.go @@ -19,17 +19,11 @@ type ExtendedWriter = network.ExtendedWriter type ExtendedReader = network.ExtendedReader func NewDeadlineConn(conn net.Conn) ExtendedConn { - if dc, ok := conn.(*deadline.Conn); ok { - return dc - } - return deadline.NewConn(conn) + return deadline.NewFallbackConn(conn) } func NewDeadlinePacketConn(pc net.PacketConn) net.PacketConn { - if dpc, ok := pc.(*deadline.PacketConn); ok { - return dpc - } - return deadline.NewPacketConn(bufio.NewPacketConn(pc)) + return deadline.NewFallbackPacketConn(bufio.NewPacketConn(pc)) } func NeedHandshake(conn any) bool { diff --git a/component/dialer/tfo.go b/component/dialer/tfo.go index 0041a976..d5337bdb 100644 --- a/component/dialer/tfo.go +++ b/component/dialer/tfo.go @@ -105,6 +105,10 @@ func (c *tfoConn) Upstream() any { return c.Conn } +func (c *tfoConn) NeedAdditionalReadDeadline() bool { + return c.Conn == nil +} + func (c *tfoConn) NeedHandshake() bool { return c.Conn == nil } diff --git a/go.mod b/go.mod index 971d4291..eb2957a7 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 - github.com/metacubex/sing-shadowsocks v0.2.2-0.20230414151724-274b69df9875 + github.com/metacubex/sing-shadowsocks v0.2.2-0.20230417103204-e2bcd32a73cc github.com/metacubex/sing-tun v0.1.3 github.com/metacubex/sing-wireguard v0.0.0-20230413082948-e51777dcf025 github.com/miekg/dns v1.1.52 @@ -28,9 +28,9 @@ require ( github.com/openacid/low v0.1.21 github.com/oschwald/geoip2-golang v1.8.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 - github.com/sagernet/sing v0.2.3 - github.com/sagernet/sing-shadowtls v0.1.1 - github.com/sagernet/sing-vmess v0.1.4 + github.com/sagernet/sing v0.2.4-0.20230417103520-0b4d134fe945 + github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b + github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c diff --git a/go.sum b/go.sum index c45880a9..45cdfeff 100644 --- a/go.sum +++ b/go.sum @@ -96,8 +96,8 @@ github.com/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03 h1:gREIdurac9fpyB github.com/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03/go.mod h1:wqEuzdImyqD2MCGE8CYRJXbB77oSEJeoSSXXdwKjnsE= github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 h1:KD96JPdTIayTGGgRl6PuVqo2Bpo6+x3LqDDyqrYDDXw= github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594/go.mod h1:9nOiGX6kqV3+ZbkDKdTNzdFD726QQHPH6WDb36jUSpA= -github.com/metacubex/sing-shadowsocks v0.2.2-0.20230414151724-274b69df9875 h1:TrTg8JY//QOu4JZuXjKzB35Gppz4B4Us9EIvtdno6vo= -github.com/metacubex/sing-shadowsocks v0.2.2-0.20230414151724-274b69df9875/go.mod h1:0ebzwmiUVa4wAmjhzaEx5BgeuhJFT5QYO0oxwolKlYE= +github.com/metacubex/sing-shadowsocks v0.2.2-0.20230417103204-e2bcd32a73cc h1:ctwahnHNAgoicCUJqKrHNVcyjEeNIoK4Dqyjh5uIkEE= +github.com/metacubex/sing-shadowsocks v0.2.2-0.20230417103204-e2bcd32a73cc/go.mod h1:4uQQReKMTU7KTfOykVBe/oGJ00pl38d+BYJ99+mx26s= github.com/metacubex/sing-tun v0.1.3 h1:LCz8TlAZUWwnBYZxs1PEE04PyfLA9xdsye6CjHZlZGQ= github.com/metacubex/sing-tun v0.1.3/go.mod h1:kHkYHoRlYA4I12QPTt/ADyRGqy5YweLUfye1E1hC/q4= github.com/metacubex/sing-wireguard v0.0.0-20230413082948-e51777dcf025 h1:itjCqEeem+BKZFiBEhQmWHDHKK4FgTGvnYCFS0JOb3A= @@ -144,12 +144,12 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= -github.com/sagernet/sing v0.2.3 h1:V50MvZ4c3Iij2lYFWPlzL1PyipwSzjGeN9x+Ox89vpk= -github.com/sagernet/sing v0.2.3/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= -github.com/sagernet/sing-shadowtls v0.1.1 h1:Gf8YD/4zCjfM9xxVzI0QVYAnjMCtsDHcIp84CUdY3VA= -github.com/sagernet/sing-shadowtls v0.1.1/go.mod h1:espnHDPRQeG95ZoU3xfHDFnc6Nt4GvbbuPV2okN+x/E= -github.com/sagernet/sing-vmess v0.1.4 h1:aZ03KgmR9COPitVqo8NTPHeaHmbJYDZ8mkumnUV4oGQ= -github.com/sagernet/sing-vmess v0.1.4/go.mod h1:NensKZJDgfPsUf3TzFD8kTuzI3CQfN1Tj3Eygti5Isg= +github.com/sagernet/sing v0.2.4-0.20230417103520-0b4d134fe945 h1:mh7/K6IU1ZJ2CKRyfnyq3G/zKQVoBSVw8oWtaR1Hj6M= +github.com/sagernet/sing v0.2.4-0.20230417103520-0b4d134fe945/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= +github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b h1:ouW/6IDCrxkBe19YSbdCd7buHix7b+UZ6BM4Zz74XF4= +github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b/go.mod h1:oG8bPerYI6cZ74KquY3DvA7ynECyrILPBnce6wtBqeI= +github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 h1:BHOnxrbC929JonuKqFdJ7ZbDp7zs4oTlH5KFvKtWu9U= +github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3/go.mod h1:yKrAr+dqZd64DxBXCHWrYicp+n4qbqO73mtwv3dck8U= github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE= github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9/go.mod h1:FUyTEc5ye5NjKnDTDMuiLF2M6T4BE6y6KZuax//UCEg= github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfIATJ8oQwBmpOZJuozQG7Vk88lL4= diff --git a/listener/shadowsocks/tcp.go b/listener/shadowsocks/tcp.go index 4e25a7b9..6884b960 100644 --- a/listener/shadowsocks/tcp.go +++ b/listener/shadowsocks/tcp.go @@ -100,7 +100,8 @@ func (l *Listener) AddrList() (addrList []net.Addr) { } func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) { - conn = N.NewDeadlineConn(l.pickCipher.StreamConn(conn)) + conn = l.pickCipher.StreamConn(conn) + conn = N.NewDeadlineConn(conn) // embed ss can't handle readDeadline correctly target, err := socks5.ReadAddr(conn, make([]byte, socks5.MaxAddrLen)) if err != nil { diff --git a/listener/shadowsocks/udp.go b/listener/shadowsocks/udp.go index d884c2b8..3f058406 100644 --- a/listener/shadowsocks/udp.go +++ b/listener/shadowsocks/udp.go @@ -4,7 +4,6 @@ import ( "net" "github.com/Dreamacro/clash/adapter/inbound" - N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/pool" "github.com/Dreamacro/clash/common/sockopt" C "github.com/Dreamacro/clash/constant" @@ -30,7 +29,7 @@ func NewUDP(addr string, pickCipher core.Cipher, in chan<- C.PacketAdapter) (*UD } sl := &UDPListener{l, false} - conn := N.NewDeadlinePacketConn(pickCipher.PacketConn(l)) + conn := pickCipher.PacketConn(l) go func() { for { buf := pool.Get(pool.RelayBufferSize) diff --git a/listener/sing/sing.go b/listener/sing/sing.go index 7421a4f1..2941f6ca 100644 --- a/listener/sing/sing.go +++ b/listener/sing/sing.go @@ -17,6 +17,7 @@ import ( vmess "github.com/sagernet/sing-vmess" "github.com/sagernet/sing/common/buf" + "github.com/sagernet/sing/common/bufio/deadline" E "github.com/sagernet/sing/common/exceptions" M "github.com/sagernet/sing/common/metadata" "github.com/sagernet/sing/common/network" @@ -80,6 +81,9 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta defer wg.Wait() // this goroutine must exit after conn.Close() wg.Add(1) + if deadline.NeedAdditionalReadDeadline(conn) { + conn = N.NewDeadlineConn(conn) // conn from sing should check NeedAdditionalReadDeadline + } h.TcpIn <- inbound.NewSocket(target, &waitCloseConn{ExtendedConn: N.NewExtendedConn(conn), wg: wg, rAddr: metadata.Source.TCPAddr()}, h.Type, additions...) return nil } From cae9cf44cfd0cba017ebb5486b7aef3a58fda6ac Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 17 Apr 2023 19:46:57 +0800 Subject: [PATCH 62/88] chore: Update dependencies --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index eb2957a7..1cf8d35a 100644 --- a/go.mod +++ b/go.mod @@ -15,15 +15,15 @@ require ( github.com/google/gopacket v1.1.19 github.com/gorilla/websocket v1.5.0 github.com/hashicorp/golang-lru v0.5.4 - github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961 + github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16 github.com/jpillora/backoff v1.0.0 github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 github.com/metacubex/sing-shadowsocks v0.2.2-0.20230417103204-e2bcd32a73cc - github.com/metacubex/sing-tun v0.1.3 + github.com/metacubex/sing-tun v0.1.4-0.20230417114323-ef78a871e13f github.com/metacubex/sing-wireguard v0.0.0-20230413082948-e51777dcf025 - github.com/miekg/dns v1.1.52 + github.com/miekg/dns v1.1.53 github.com/mroth/weightedrand/v2 v2.0.0 github.com/openacid/low v0.1.21 github.com/oschwald/geoip2-golang v1.8.0 @@ -34,7 +34,7 @@ require ( github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c - github.com/samber/lo v1.37.0 + github.com/samber/lo v1.38.1 github.com/shirou/gopsutil/v3 v3.23.3 github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.2 @@ -75,7 +75,7 @@ require ( github.com/klauspost/cpuid/v2 v2.0.12 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // 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-20230417114019-3c3ee672d60c // indirect github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect github.com/onsi/ginkgo/v2 v2.2.0 // indirect diff --git a/go.sum b/go.sum index 45cdfeff..553498a8 100644 --- a/go.sum +++ b/go.sum @@ -68,8 +68,8 @@ github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961 h1:x/YtdDlmypenG1te/FfH6LVM+3krhXk5CFV8VYNNX5M= -github.com/insomniacslk/dhcp v0.0.0-20230307103557-e252950ab961/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI= +github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16 h1:+aAGyK41KRn8jbF2Q7PLL0Sxwg6dShGcQSeCC7nZQ8E= +github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= @@ -92,20 +92,20 @@ github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 h1:HSkXG1bE/qcR github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7/go.mod h1:1ztDZHGbU5MjN5lNZpkpG8ygndjjWzcojp/H7r6l6QQ= github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= -github.com/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03 h1:gREIdurac9fpyBMBRPPMF/Sk3gKfPfdNCa4GQyR9FoA= -github.com/metacubex/gvisor v0.0.0-20230323114922-412956fb6a03/go.mod h1:wqEuzdImyqD2MCGE8CYRJXbB77oSEJeoSSXXdwKjnsE= +github.com/metacubex/gvisor v0.0.0-20230417114019-3c3ee672d60c h1:D62872jiuzC6b+3aI8tqfeyc6YgbfarYKywTnnvXwEM= +github.com/metacubex/gvisor v0.0.0-20230417114019-3c3ee672d60c/go.mod h1:wqEuzdImyqD2MCGE8CYRJXbB77oSEJeoSSXXdwKjnsE= github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 h1:KD96JPdTIayTGGgRl6PuVqo2Bpo6+x3LqDDyqrYDDXw= github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594/go.mod h1:9nOiGX6kqV3+ZbkDKdTNzdFD726QQHPH6WDb36jUSpA= github.com/metacubex/sing-shadowsocks v0.2.2-0.20230417103204-e2bcd32a73cc h1:ctwahnHNAgoicCUJqKrHNVcyjEeNIoK4Dqyjh5uIkEE= github.com/metacubex/sing-shadowsocks v0.2.2-0.20230417103204-e2bcd32a73cc/go.mod h1:4uQQReKMTU7KTfOykVBe/oGJ00pl38d+BYJ99+mx26s= -github.com/metacubex/sing-tun v0.1.3 h1:LCz8TlAZUWwnBYZxs1PEE04PyfLA9xdsye6CjHZlZGQ= -github.com/metacubex/sing-tun v0.1.3/go.mod h1:kHkYHoRlYA4I12QPTt/ADyRGqy5YweLUfye1E1hC/q4= +github.com/metacubex/sing-tun v0.1.4-0.20230417114323-ef78a871e13f h1:vS2bII9IjwpNaf311kWn9hi7p6WSzHM4dFdvmYECvEs= +github.com/metacubex/sing-tun v0.1.4-0.20230417114323-ef78a871e13f/go.mod h1:ifLPYzRpV/u8bLE39zGb4nuX1NuUqzzLZmF8iZvxjIA= github.com/metacubex/sing-wireguard v0.0.0-20230413082948-e51777dcf025 h1:itjCqEeem+BKZFiBEhQmWHDHKK4FgTGvnYCFS0JOb3A= github.com/metacubex/sing-wireguard v0.0.0-20230413082948-e51777dcf025/go.mod h1:7mPG9qYln+CLKBcDt7Dk4c7b3S53VzEfexMVPe6T6FM= github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370 h1:UkViS4DCESAUEYgbIEQdD02hyMacFt6Dny+1MOJtNIo= github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -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.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw= +github.com/miekg/dns v1.1.53/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= @@ -156,8 +156,8 @@ github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfI github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM= github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo= github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI= -github.com/samber/lo v1.37.0 h1:XjVcB8g6tgUp8rsPsJ2CvhClfImrpL04YpQHXeHPhRw= -github.com/samber/lo v1.37.0/go.mod h1:9vaz2O4o8oOnK23pd2TrXufcbdbJIa3b6cstBWKpopA= +github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= +github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/shirou/gopsutil/v3 v3.23.3 h1:Syt5vVZXUDXPEXpIBt5ziWsJ4LdSAAxF4l/xZeQgSEE= github.com/shirou/gopsutil/v3 v3.23.3/go.mod h1:lSBNN6t3+D6W5e5nXTxc8KIMMVxAcS+6IJlffjRRlMU= github.com/shoenig/go-m1cpu v0.1.4/go.mod h1:Wwvst4LR89UxjeFtLRMrpgRiyY4xPsejnVZym39dbAQ= From f100ce6a04818e1aad53b7854c93bb85d2442353 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 17 Apr 2023 20:38:37 +0800 Subject: [PATCH 63/88] chore: Adopt sing-tun's update --- go.mod | 2 +- go.sum | 4 ++-- listener/sing_tun/server.go | 15 ++++++++++++--- listener/sing_tun/tun_name_darwin.go | 11 +++++++++++ listener/sing_tun/tun_name_linux.go | 25 +++++++++++++++++++++++++ listener/sing_tun/tun_name_other.go | 9 +++++++++ 6 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 listener/sing_tun/tun_name_darwin.go create mode 100644 listener/sing_tun/tun_name_linux.go create mode 100644 listener/sing_tun/tun_name_other.go diff --git a/go.mod b/go.mod index 1cf8d35a..fc2783f7 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 github.com/metacubex/sing-shadowsocks v0.2.2-0.20230417103204-e2bcd32a73cc - github.com/metacubex/sing-tun v0.1.4-0.20230417114323-ef78a871e13f + github.com/metacubex/sing-tun v0.1.4-0.20230417120711-05d852989e84 github.com/metacubex/sing-wireguard v0.0.0-20230413082948-e51777dcf025 github.com/miekg/dns v1.1.53 github.com/mroth/weightedrand/v2 v2.0.0 diff --git a/go.sum b/go.sum index 553498a8..f42dba4c 100644 --- a/go.sum +++ b/go.sum @@ -98,8 +98,8 @@ github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 h1:KD96JPdTIa github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594/go.mod h1:9nOiGX6kqV3+ZbkDKdTNzdFD726QQHPH6WDb36jUSpA= github.com/metacubex/sing-shadowsocks v0.2.2-0.20230417103204-e2bcd32a73cc h1:ctwahnHNAgoicCUJqKrHNVcyjEeNIoK4Dqyjh5uIkEE= github.com/metacubex/sing-shadowsocks v0.2.2-0.20230417103204-e2bcd32a73cc/go.mod h1:4uQQReKMTU7KTfOykVBe/oGJ00pl38d+BYJ99+mx26s= -github.com/metacubex/sing-tun v0.1.4-0.20230417114323-ef78a871e13f h1:vS2bII9IjwpNaf311kWn9hi7p6WSzHM4dFdvmYECvEs= -github.com/metacubex/sing-tun v0.1.4-0.20230417114323-ef78a871e13f/go.mod h1:ifLPYzRpV/u8bLE39zGb4nuX1NuUqzzLZmF8iZvxjIA= +github.com/metacubex/sing-tun v0.1.4-0.20230417120711-05d852989e84 h1:3L4glttGciACRBYV1Z1eglcYFYWiXmTExWjTySgaGdM= +github.com/metacubex/sing-tun v0.1.4-0.20230417120711-05d852989e84/go.mod h1:kHkYHoRlYA4I12QPTt/ADyRGqy5YweLUfye1E1hC/q4= github.com/metacubex/sing-wireguard v0.0.0-20230413082948-e51777dcf025 h1:itjCqEeem+BKZFiBEhQmWHDHKK4FgTGvnYCFS0JOb3A= github.com/metacubex/sing-wireguard v0.0.0-20230413082948-e51777dcf025/go.mod h1:7mPG9qYln+CLKBcDt7Dk4c7b3S53VzEfexMVPe6T6FM= github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370 h1:UkViS4DCESAUEYgbIEQdD02hyMacFt6Dny+1MOJtNIo= diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index ada9d1c2..5f691b44 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -229,8 +229,8 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte err = E.Cause(err, "configure tun interface") return } - l.tunIf = tunIf - l.tunStack, err = tun.NewStack(strings.ToLower(options.Stack.String()), tun.StackOptions{ + + stackOptions := tun.StackOptions{ Context: context.TODO(), Tun: tunIf, MTU: tunOptions.MTU, @@ -241,7 +241,16 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte UDPTimeout: udpTimeout, Handler: handler, Logger: log.SingLogger, - }) + } + + if options.FileDescriptor > 0 { + if tunName, err := getTunnelName(int32(options.FileDescriptor)); err != nil { + stackOptions.Name = tunName + stackOptions.UnderPlatform = true + } + } + l.tunIf = tunIf + l.tunStack, err = tun.NewStack(strings.ToLower(options.Stack.String()), stackOptions) if err != nil { return } diff --git a/listener/sing_tun/tun_name_darwin.go b/listener/sing_tun/tun_name_darwin.go new file mode 100644 index 00000000..5b4686ea --- /dev/null +++ b/listener/sing_tun/tun_name_darwin.go @@ -0,0 +1,11 @@ +package sing_tun + +import "golang.org/x/sys/unix" + +func getTunnelName(fd int32) (string, error) { + return unix.GetsockoptString( + int(fd), + 2, /* #define SYSPROTO_CONTROL 2 */ + 2, /* #define UTUN_OPT_IFNAME 2 */ + ) +} diff --git a/listener/sing_tun/tun_name_linux.go b/listener/sing_tun/tun_name_linux.go new file mode 100644 index 00000000..9ae9800b --- /dev/null +++ b/listener/sing_tun/tun_name_linux.go @@ -0,0 +1,25 @@ +package sing_tun + +import ( + "fmt" + "golang.org/x/sys/unix" + "syscall" + "unsafe" +) + +const ifReqSize = unix.IFNAMSIZ + 64 + +func getTunnelName(fd int32) (string, error) { + var ifr [ifReqSize]byte + var errno syscall.Errno + _, _, errno = unix.Syscall( + unix.SYS_IOCTL, + uintptr(fd), + uintptr(unix.TUNGETIFF), + uintptr(unsafe.Pointer(&ifr[0])), + ) + if errno != 0 { + return "", fmt.Errorf("failed to get name of TUN device: %w", errno) + } + return unix.ByteSliceToString(ifr[:]), nil +} diff --git a/listener/sing_tun/tun_name_other.go b/listener/sing_tun/tun_name_other.go new file mode 100644 index 00000000..c47c8cbe --- /dev/null +++ b/listener/sing_tun/tun_name_other.go @@ -0,0 +1,9 @@ +//go:build !(darwin || linux) + +package sing_tun + +import "os" + +func getTunnelName(fd int32) (string, error) { + return "", os.ErrInvalid +} From 47df97322db5e4dc28a4c39441c27b401bf53ca6 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 17 Apr 2023 23:42:15 +0800 Subject: [PATCH 64/88] fix: h2 close panic --- transport/vmess/h2.go | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/transport/vmess/h2.go b/transport/vmess/h2.go index 6901f61e..f91c2766 100644 --- a/transport/vmess/h2.go +++ b/transport/vmess/h2.go @@ -1,6 +1,7 @@ package vmess import ( + "context" "io" "net" "net/http" @@ -84,10 +85,16 @@ func (hc *h2Conn) Write(b []byte) (int, error) { } func (hc *h2Conn) Close() error { - if err := hc.pwriter.Close(); err != nil { - return err + if hc.pwriter != nil { + if err := hc.pwriter.Close(); err != nil { + return err + } } - if err := hc.ClientConn.Shutdown(hc.res.Request.Context()); err != nil { + ctx := context.Background() + if hc.res != nil { + ctx = hc.res.Request.Context() + } + if err := hc.ClientConn.Shutdown(ctx); err != nil { return err } return hc.Conn.Close() From 4ef99294e7441fdf1242744684b003a3f75840c9 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 19 Apr 2023 19:25:21 +0800 Subject: [PATCH 65/88] chore: rewrite verifyIP6 --- config/utils.go | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/config/utils.go b/config/utils.go index 2c470618..799082c4 100644 --- a/config/utils.go +++ b/config/utils.go @@ -3,6 +3,7 @@ package config import ( "fmt" "net" + "net/netip" "strings" "github.com/Dreamacro/clash/adapter/outboundgroup" @@ -149,20 +150,11 @@ func proxyGroupsDagSort(groupsConfig []map[string]any) error { } func verifyIP6() bool { - addrs, err := net.InterfaceAddrs() - if err != nil { - return false - } - for _, addr := range addrs { - ipNet, isIpNet := addr.(*net.IPNet) - if isIpNet && !ipNet.IP.IsLoopback() { - if ipNet.IP.To16() != nil { - s := ipNet.IP.String() - for i := 0; i < len(s); i++ { - switch s[i] { - case ':': - return true - } + if iAddrs, err := net.InterfaceAddrs(); err == nil { + for _, addr := range iAddrs { + if prefix, err := netip.ParsePrefix(addr.String()); err == nil { + if addr := prefix.Addr().Unmap(); addr.Is6() && addr.IsGlobalUnicast() { + return true } } } From dd60baf9d4c417f5e3733a2dad28f7443da62874 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 19 Apr 2023 23:50:57 +0800 Subject: [PATCH 66/88] fix: revert LRU cache changes --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index fc2783f7..fe3940e4 100644 --- a/go.mod +++ b/go.mod @@ -20,15 +20,15 @@ require ( github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 - github.com/metacubex/sing-shadowsocks v0.2.2-0.20230417103204-e2bcd32a73cc - github.com/metacubex/sing-tun v0.1.4-0.20230417120711-05d852989e84 + github.com/metacubex/sing-shadowsocks v0.2.2-0.20230419154412-11ec6808f588 + github.com/metacubex/sing-tun v0.1.4-0.20230419154912-049c66801ff8 github.com/metacubex/sing-wireguard v0.0.0-20230413082948-e51777dcf025 github.com/miekg/dns v1.1.53 github.com/mroth/weightedrand/v2 v2.0.0 github.com/openacid/low v0.1.21 github.com/oschwald/geoip2-golang v1.8.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 - github.com/sagernet/sing v0.2.4-0.20230417103520-0b4d134fe945 + github.com/sagernet/sing v0.2.4-0.20230419153323-5fae6fa434c1 github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 diff --git a/go.sum b/go.sum index f42dba4c..9dcbe783 100644 --- a/go.sum +++ b/go.sum @@ -96,10 +96,10 @@ github.com/metacubex/gvisor v0.0.0-20230417114019-3c3ee672d60c h1:D62872jiuzC6b+ github.com/metacubex/gvisor v0.0.0-20230417114019-3c3ee672d60c/go.mod h1:wqEuzdImyqD2MCGE8CYRJXbB77oSEJeoSSXXdwKjnsE= github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 h1:KD96JPdTIayTGGgRl6PuVqo2Bpo6+x3LqDDyqrYDDXw= github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594/go.mod h1:9nOiGX6kqV3+ZbkDKdTNzdFD726QQHPH6WDb36jUSpA= -github.com/metacubex/sing-shadowsocks v0.2.2-0.20230417103204-e2bcd32a73cc h1:ctwahnHNAgoicCUJqKrHNVcyjEeNIoK4Dqyjh5uIkEE= -github.com/metacubex/sing-shadowsocks v0.2.2-0.20230417103204-e2bcd32a73cc/go.mod h1:4uQQReKMTU7KTfOykVBe/oGJ00pl38d+BYJ99+mx26s= -github.com/metacubex/sing-tun v0.1.4-0.20230417120711-05d852989e84 h1:3L4glttGciACRBYV1Z1eglcYFYWiXmTExWjTySgaGdM= -github.com/metacubex/sing-tun v0.1.4-0.20230417120711-05d852989e84/go.mod h1:kHkYHoRlYA4I12QPTt/ADyRGqy5YweLUfye1E1hC/q4= +github.com/metacubex/sing-shadowsocks v0.2.2-0.20230419154412-11ec6808f588 h1:bOuVbeG5iwYyiDB2am3xIrt7GwbZC8cZIoJqLtvkcfY= +github.com/metacubex/sing-shadowsocks v0.2.2-0.20230419154412-11ec6808f588/go.mod h1:4uQQReKMTU7KTfOykVBe/oGJ00pl38d+BYJ99+mx26s= +github.com/metacubex/sing-tun v0.1.4-0.20230419154912-049c66801ff8 h1:BQFEnZUnPvmER8JIQPsfaaWzMXOvY5o+2ToDcRfAh58= +github.com/metacubex/sing-tun v0.1.4-0.20230419154912-049c66801ff8/go.mod h1:5CR2e0WQQiLSPd46Qaz3uhDRRik3QvvXiyMvlT3bXoc= github.com/metacubex/sing-wireguard v0.0.0-20230413082948-e51777dcf025 h1:itjCqEeem+BKZFiBEhQmWHDHKK4FgTGvnYCFS0JOb3A= github.com/metacubex/sing-wireguard v0.0.0-20230413082948-e51777dcf025/go.mod h1:7mPG9qYln+CLKBcDt7Dk4c7b3S53VzEfexMVPe6T6FM= github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370 h1:UkViS4DCESAUEYgbIEQdD02hyMacFt6Dny+1MOJtNIo= @@ -144,8 +144,8 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= -github.com/sagernet/sing v0.2.4-0.20230417103520-0b4d134fe945 h1:mh7/K6IU1ZJ2CKRyfnyq3G/zKQVoBSVw8oWtaR1Hj6M= -github.com/sagernet/sing v0.2.4-0.20230417103520-0b4d134fe945/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= +github.com/sagernet/sing v0.2.4-0.20230419153323-5fae6fa434c1 h1:CdzNL25lzfVo0NMeghPqsupNsWvkzrbrUt5t8DoDPcQ= +github.com/sagernet/sing v0.2.4-0.20230419153323-5fae6fa434c1/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b h1:ouW/6IDCrxkBe19YSbdCd7buHix7b+UZ6BM4Zz74XF4= github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b/go.mod h1:oG8bPerYI6cZ74KquY3DvA7ynECyrILPBnce6wtBqeI= github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 h1:BHOnxrbC929JonuKqFdJ7ZbDp7zs4oTlH5KFvKtWu9U= From 31095e4a8cf72d7f3b7c822d03b37e1cab702d46 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 20 Apr 2023 09:38:08 +0800 Subject: [PATCH 67/88] fix: vless udp not working --- adapter/outbound/vless.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index 5f7c2087..d7db27d5 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -174,17 +174,18 @@ func (v *Vless) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { func (v *Vless) streamConn(c net.Conn, metadata *C.Metadata) (conn net.Conn, err error) { if metadata.NetWork == C.UDP { - metadata = &C.Metadata{ // a clear metadata only contains ip - NetWork: metadata.NetWork, - DstIP: metadata.DstIP, - DstPort: metadata.DstPort, - } if v.option.PacketAddr { metadata = &C.Metadata{ NetWork: C.UDP, Host: packetaddr.SeqPacketMagicAddress, DstPort: "443", } + } else { + metadata = &C.Metadata{ // a clear metadata only contains ip + NetWork: C.UDP, + DstIP: metadata.DstIP, + DstPort: metadata.DstPort, + } } conn, err = v.client.StreamConn(c, parseVlessAddr(metadata, v.option.XUDP)) if v.option.PacketAddr { @@ -346,7 +347,7 @@ func (v *Vless) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met safeConnClose(c, err) }(c) - c, err = v.streamConn(c, metadata) + c, err = v.StreamConn(c, metadata) if err != nil { return nil, fmt.Errorf("new vless client error: %v", err) } @@ -372,13 +373,13 @@ func (v *Vless) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metada if v.option.XUDP { return newPacketConn(&threadSafePacketConn{ - PacketConn: vmessSing.NewXUDPConn(c, M.ParseSocksaddr(metadata.RemoteAddress())), + PacketConn: vmessSing.NewXUDPConn(c, M.SocksaddrFromNet(metadata.UDPAddr())), }, v), nil } else if v.option.PacketAddr { return newPacketConn(&threadSafePacketConn{ PacketConn: packetaddr.NewConn(&vlessPacketConn{ Conn: c, rAddr: metadata.UDPAddr(), - }, M.ParseSocksaddr(metadata.RemoteAddress())), + }, M.SocksaddrFromNet(metadata.UDPAddr())), }, v), nil } return newPacketConn(&vlessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil From ec234ac0a88a16d24bbffc7e8a80f243d41dd7dd Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 20 Apr 2023 10:22:51 +0800 Subject: [PATCH 68/88] chore: clear windows bind error --- component/dialer/bind_windows.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/component/dialer/bind_windows.go b/component/dialer/bind_windows.go index 4a099169..0d38d1c5 100644 --- a/component/dialer/bind_windows.go +++ b/component/dialer/bind_windows.go @@ -3,6 +3,7 @@ package dialer import ( "context" "encoding/binary" + "fmt" "net" "net/netip" "syscall" @@ -20,11 +21,19 @@ func bind4(handle syscall.Handle, ifaceIdx int) error { var bytes [4]byte binary.BigEndian.PutUint32(bytes[:], uint32(ifaceIdx)) idx := *(*uint32)(unsafe.Pointer(&bytes[0])) - return syscall.SetsockoptInt(handle, syscall.IPPROTO_IP, IP_UNICAST_IF, int(idx)) + err := syscall.SetsockoptInt(handle, syscall.IPPROTO_IP, IP_UNICAST_IF, int(idx)) + if err != nil { + err = fmt.Errorf("bind4: %w", err) + } + return err } func bind6(handle syscall.Handle, ifaceIdx int) error { - return syscall.SetsockoptInt(handle, syscall.IPPROTO_IPV6, IPV6_UNICAST_IF, ifaceIdx) + err := syscall.SetsockoptInt(handle, syscall.IPPROTO_IPV6, IPV6_UNICAST_IF, ifaceIdx) + if err != nil { + err = fmt.Errorf("bind6: %w", err) + } + return err } func bindControl(ifaceIdx int) controlFn { @@ -49,9 +58,9 @@ func bindControl(ifaceIdx int) controlFn { if (!addrPort.Addr().IsValid() || addrPort.Addr().IsUnspecified()) && bind6err != nil { // try bind ipv6, if failed, ignore. it's a workaround for windows disable interface ipv6 if bind4err != nil { - innerErr = bind6err + innerErr = fmt.Errorf("%w (%s)", bind6err, bind4err) } else { - innerErr = bind4err + innerErr = nil } } else { innerErr = bind6err From 75137615406a134924c3457c9315d24b96445ce4 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Thu, 20 Apr 2023 05:45:22 +0000 Subject: [PATCH 69/88] fix: not match top domain --- component/sniffer/dispatcher.go | 16 ++++++++-------- component/trie/domain.go | 5 ++++- component/trie/domain_set_test.go | 8 ++++++++ 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/component/sniffer/dispatcher.go b/component/sniffer/dispatcher.go index bf0b1bb3..fa1c6827 100644 --- a/component/sniffer/dispatcher.go +++ b/component/sniffer/dispatcher.go @@ -26,14 +26,14 @@ var ( var Dispatcher *SnifferDispatcher type SnifferDispatcher struct { - enable bool - sniffers map[sniffer.Sniffer]SnifferConfig - forceDomain *trie.DomainSet - skipSNI *trie.DomainSet - skipList *cache.LruCache[string, uint8] - rwMux sync.RWMutex - forceDnsMapping bool - parsePureIp bool + enable bool + sniffers map[sniffer.Sniffer]SnifferConfig + forceDomain *trie.DomainSet + skipSNI *trie.DomainSet + skipList *cache.LruCache[string, uint8] + rwMux sync.RWMutex + forceDnsMapping bool + parsePureIp bool } func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) { diff --git a/component/trie/domain.go b/component/trie/domain.go index 86e5245a..3decbb02 100644 --- a/component/trie/domain.go +++ b/component/trie/domain.go @@ -25,7 +25,7 @@ func ValidAndSplitDomain(domain string) ([]string, bool) { if domain != "" && domain[len(domain)-1] == '.' { return nil, false } - domain=strings.ToLower(domain) + domain = strings.ToLower(domain) parts := strings.Split(domain, domainStep) if len(parts) == 1 { if parts[0] == "" { @@ -126,6 +126,9 @@ func (t *DomainTrie[T]) Optimize() { func (t *DomainTrie[T]) Foreach(print func(domain string, data T)) { for key, data := range t.root.getChildren() { recursion([]string{key}, data, print) + if data != nil && data.inited { + print(joinDomain([]string{key}), data.data) + } } } diff --git a/component/trie/domain_set_test.go b/component/trie/domain_set_test.go index 090bd495..c4160f6c 100644 --- a/component/trie/domain_set_test.go +++ b/component/trie/domain_set_test.go @@ -15,6 +15,9 @@ func TestDomainSet(t *testing.T) { "www.google.com", "test.a.net", "test.a.oc", + "Mijia Cloud", + ".qq.com", + "+.cn", } for _, domain := range domainSet { @@ -22,8 +25,13 @@ func TestDomainSet(t *testing.T) { } set := tree.NewDomainSet() assert.NotNil(t, set) + assert.True(t, set.Has("test.cn")) + assert.True(t, set.Has("cn")) + assert.True(t, set.Has("Mijia Cloud")) assert.True(t, set.Has("test.a.net")) + assert.True(t, set.Has("www.qq.com")) assert.True(t, set.Has("google.com")) + assert.False(t, set.Has("qq.com")) assert.False(t, set.Has("www.baidu.com")) } From 00939da40f1b2467a426bc8cc608a1da06f55923 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 20 Apr 2023 13:41:55 +0800 Subject: [PATCH 70/88] chore: update wireguard-go --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- listener/sing_tun/server.go | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index fe3940e4..ecc269df 100644 --- a/go.mod +++ b/go.mod @@ -21,19 +21,19 @@ require ( github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 github.com/metacubex/sing-shadowsocks v0.2.2-0.20230419154412-11ec6808f588 - github.com/metacubex/sing-tun v0.1.4-0.20230419154912-049c66801ff8 - github.com/metacubex/sing-wireguard v0.0.0-20230413082948-e51777dcf025 + github.com/metacubex/sing-tun v0.1.4-0.20230420054006-376d37578990 + github.com/metacubex/sing-wireguard v0.0.0-20230420053659-05c12d25b6eb github.com/miekg/dns v1.1.53 github.com/mroth/weightedrand/v2 v2.0.0 github.com/openacid/low v0.1.21 github.com/oschwald/geoip2-golang v1.8.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 - github.com/sagernet/sing v0.2.4-0.20230419153323-5fae6fa434c1 + github.com/sagernet/sing v0.2.4-0.20230420044236-72471d9b35b5 github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 - github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c + github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 github.com/samber/lo v1.38.1 github.com/shirou/gopsutil/v3 v3.23.3 github.com/sirupsen/logrus v1.9.0 diff --git a/go.sum b/go.sum index 9dcbe783..a6a61afd 100644 --- a/go.sum +++ b/go.sum @@ -98,10 +98,10 @@ github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 h1:KD96JPdTIa github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594/go.mod h1:9nOiGX6kqV3+ZbkDKdTNzdFD726QQHPH6WDb36jUSpA= github.com/metacubex/sing-shadowsocks v0.2.2-0.20230419154412-11ec6808f588 h1:bOuVbeG5iwYyiDB2am3xIrt7GwbZC8cZIoJqLtvkcfY= github.com/metacubex/sing-shadowsocks v0.2.2-0.20230419154412-11ec6808f588/go.mod h1:4uQQReKMTU7KTfOykVBe/oGJ00pl38d+BYJ99+mx26s= -github.com/metacubex/sing-tun v0.1.4-0.20230419154912-049c66801ff8 h1:BQFEnZUnPvmER8JIQPsfaaWzMXOvY5o+2ToDcRfAh58= -github.com/metacubex/sing-tun v0.1.4-0.20230419154912-049c66801ff8/go.mod h1:5CR2e0WQQiLSPd46Qaz3uhDRRik3QvvXiyMvlT3bXoc= -github.com/metacubex/sing-wireguard v0.0.0-20230413082948-e51777dcf025 h1:itjCqEeem+BKZFiBEhQmWHDHKK4FgTGvnYCFS0JOb3A= -github.com/metacubex/sing-wireguard v0.0.0-20230413082948-e51777dcf025/go.mod h1:7mPG9qYln+CLKBcDt7Dk4c7b3S53VzEfexMVPe6T6FM= +github.com/metacubex/sing-tun v0.1.4-0.20230420054006-376d37578990 h1:VpC69WWsOyhwwRkMVop6ao30dh4ZtlqaroT5uK6EBYU= +github.com/metacubex/sing-tun v0.1.4-0.20230420054006-376d37578990/go.mod h1:5CR2e0WQQiLSPd46Qaz3uhDRRik3QvvXiyMvlT3bXoc= +github.com/metacubex/sing-wireguard v0.0.0-20230420053659-05c12d25b6eb h1:uoeOqHaQmNPev9RewQlff9Z55yuczEhRizcgDf4lSAw= +github.com/metacubex/sing-wireguard v0.0.0-20230420053659-05c12d25b6eb/go.mod h1:Bsw2BvKMMMY0FhZPseDI50ZOalvoUPMKYyGpyqvIIqY= github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370 h1:UkViS4DCESAUEYgbIEQdD02hyMacFt6Dny+1MOJtNIo= github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw= @@ -144,8 +144,8 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= -github.com/sagernet/sing v0.2.4-0.20230419153323-5fae6fa434c1 h1:CdzNL25lzfVo0NMeghPqsupNsWvkzrbrUt5t8DoDPcQ= -github.com/sagernet/sing v0.2.4-0.20230419153323-5fae6fa434c1/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= +github.com/sagernet/sing v0.2.4-0.20230420044236-72471d9b35b5 h1:UtihSORXefZYQrGIssu65bP18VEgMGCNHdy60VHc14M= +github.com/sagernet/sing v0.2.4-0.20230420044236-72471d9b35b5/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b h1:ouW/6IDCrxkBe19YSbdCd7buHix7b+UZ6BM4Zz74XF4= github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b/go.mod h1:oG8bPerYI6cZ74KquY3DvA7ynECyrILPBnce6wtBqeI= github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 h1:BHOnxrbC929JonuKqFdJ7ZbDp7zs4oTlH5KFvKtWu9U= @@ -154,8 +154,8 @@ github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBT github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9/go.mod h1:FUyTEc5ye5NjKnDTDMuiLF2M6T4BE6y6KZuax//UCEg= github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfIATJ8oQwBmpOZJuozQG7Vk88lL4= github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM= -github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c h1:vK2wyt9aWYHHvNLWniwijBu/n4pySypiKRhN32u/JGo= -github.com/sagernet/wireguard-go v0.0.0-20221116151939-c99467f53f2c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI= +github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77 h1:g6QtRWQ2dKX7EQP++1JLNtw4C2TNxd4/ov8YUpOPOSo= +github.com/sagernet/wireguard-go v0.0.0-20230420044414-a7bac1754e77/go.mod h1:pJDdXzZIwJ+2vmnT0TKzmf8meeum+e2mTDSehw79eE0= github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/shirou/gopsutil/v3 v3.23.3 h1:Syt5vVZXUDXPEXpIBt5ziWsJ4LdSAAxF4l/xZeQgSEE= diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 5f691b44..084c701b 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -246,7 +246,7 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte if options.FileDescriptor > 0 { if tunName, err := getTunnelName(int32(options.FileDescriptor)); err != nil { stackOptions.Name = tunName - stackOptions.UnderPlatform = true + stackOptions.ForwarderBindInterface = true } } l.tunIf = tunIf From c6ecbb25dcc0c588eced38029ee34d6d3948012c Mon Sep 17 00:00:00 2001 From: Skyxim Date: Fri, 21 Apr 2023 08:49:03 +0000 Subject: [PATCH 71/88] chore: update doc --- docs/config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/config.yaml b/docs/config.yaml index 922d4895..fe4ee682 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -594,6 +594,7 @@ proxies: # socks5 type: hysteria server: server.com port: 443 + # ports: 1000,2000-3000,5000 # port 不可省略, auth_str: yourpassword # 将会在未来某个时候删除 # auth-str: yourpassword # obfs: obfs_str From 40da1911d902d923f19d2999562b820aba47d4de Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 22 Apr 2023 15:37:57 +0800 Subject: [PATCH 72/88] chore: using sync/atomic replace uber/atomic --- adapter/adapter.go | 8 +- adapter/outboundgroup/groupbase.go | 2 +- adapter/provider/healthcheck.go | 3 +- common/atomic/type.go | 205 +++++++++++++++++++++++++++ common/atomic/value.go | 58 ++++++++ common/observable/observable_test.go | 5 +- common/singledo/singledo_test.go | 5 +- component/dialer/options.go | 5 +- component/profile/profile.go | 2 +- dns/client.go | 4 +- dns/dhcp.go | 5 +- dns/resolver.go | 5 +- go.mod | 3 - go.sum | 2 - transport/gun/gun.go | 2 +- tunnel/statistic/manager.go | 3 +- tunnel/statistic/tracker.go | 2 +- 17 files changed, 288 insertions(+), 31 deletions(-) create mode 100644 common/atomic/type.go create mode 100644 common/atomic/value.go diff --git a/adapter/adapter.go b/adapter/adapter.go index ffb5ced0..538ba271 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -4,16 +4,16 @@ import ( "context" "encoding/json" "fmt" - "github.com/Dreamacro/clash/common/queue" - "github.com/Dreamacro/clash/component/dialer" - C "github.com/Dreamacro/clash/constant" "net" "net/http" "net/netip" "net/url" "time" - "go.uber.org/atomic" + "github.com/Dreamacro/clash/common/atomic" + "github.com/Dreamacro/clash/common/queue" + "github.com/Dreamacro/clash/component/dialer" + C "github.com/Dreamacro/clash/constant" ) var UnifiedDelay = atomic.NewBool(false) diff --git a/adapter/outboundgroup/groupbase.go b/adapter/outboundgroup/groupbase.go index 0a421793..8e253e63 100644 --- a/adapter/outboundgroup/groupbase.go +++ b/adapter/outboundgroup/groupbase.go @@ -8,6 +8,7 @@ import ( "time" "github.com/Dreamacro/clash/adapter/outbound" + "github.com/Dreamacro/clash/common/atomic" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/constant/provider" types "github.com/Dreamacro/clash/constant/provider" @@ -15,7 +16,6 @@ import ( "github.com/Dreamacro/clash/tunnel" "github.com/dlclark/regexp2" - "go.uber.org/atomic" ) type GroupBase struct { diff --git a/adapter/provider/healthcheck.go b/adapter/provider/healthcheck.go index 9deb7b82..fa13e32e 100644 --- a/adapter/provider/healthcheck.go +++ b/adapter/provider/healthcheck.go @@ -4,13 +4,12 @@ import ( "context" "time" + "github.com/Dreamacro/clash/common/atomic" "github.com/Dreamacro/clash/common/batch" "github.com/Dreamacro/clash/common/singledo" "github.com/Dreamacro/clash/common/utils" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" - - "go.uber.org/atomic" ) const ( diff --git a/common/atomic/type.go b/common/atomic/type.go new file mode 100644 index 00000000..f1549235 --- /dev/null +++ b/common/atomic/type.go @@ -0,0 +1,205 @@ +package atomic + +import ( + "encoding/json" + "fmt" + "strconv" + "sync/atomic" +) + +type Bool struct { + atomic.Bool +} + +func NewBool(val bool) *Bool { + i := &Bool{} + i.Store(val) + return i +} + +func (i *Bool) MarshalJSON() ([]byte, error) { + return json.Marshal(i.Load()) +} + +func (i *Bool) UnmarshalJSON(b []byte) error { + var v bool + if err := json.Unmarshal(b, &v); err != nil { + return err + } + i.Store(v) + return nil +} + +func (i *Bool) String() string { + v := i.Load() + return strconv.FormatBool(v) +} + +type Pointer[T any] struct { + atomic.Pointer[T] +} + +func NewPointer[T any](v *T) *Pointer[T] { + var p Pointer[T] + if v != nil { + p.Store(v) + } + return &p +} + +func (p *Pointer[T]) MarshalJSON() ([]byte, error) { + return json.Marshal(p.Load()) +} + +func (p *Pointer[T]) UnmarshalJSON(b []byte) error { + var v *T + if err := json.Unmarshal(b, &v); err != nil { + return err + } + p.Store(v) + return nil +} + +func (p *Pointer[T]) String() string { + return fmt.Sprint(p.Load()) +} + +type Int32 struct { + atomic.Int32 +} + +func NewInt32(val int32) *Int32 { + i := &Int32{} + i.Store(val) + return i +} + +func (i *Int32) MarshalJSON() ([]byte, error) { + return json.Marshal(i.Load()) +} + +func (i *Int32) UnmarshalJSON(b []byte) error { + var v int32 + if err := json.Unmarshal(b, &v); err != nil { + return err + } + i.Store(v) + return nil +} + +func (i *Int32) String() string { + v := i.Load() + return strconv.FormatInt(int64(v), 10) +} + +type Int64 struct { + atomic.Int64 +} + +func NewInt64(val int64) *Int64 { + i := &Int64{} + i.Store(val) + return i +} + +func (i *Int64) MarshalJSON() ([]byte, error) { + return json.Marshal(i.Load()) +} + +func (i *Int64) UnmarshalJSON(b []byte) error { + var v int64 + if err := json.Unmarshal(b, &v); err != nil { + return err + } + i.Store(v) + return nil +} + +func (i *Int64) String() string { + v := i.Load() + return strconv.FormatInt(int64(v), 10) +} + +type Uint32 struct { + atomic.Uint32 +} + +func NewUint32(val uint32) *Uint32 { + i := &Uint32{} + i.Store(val) + return i +} + +func (i *Uint32) MarshalJSON() ([]byte, error) { + return json.Marshal(i.Load()) +} + +func (i *Uint32) UnmarshalJSON(b []byte) error { + var v uint32 + if err := json.Unmarshal(b, &v); err != nil { + return err + } + i.Store(v) + return nil +} + +func (i *Uint32) String() string { + v := i.Load() + return strconv.FormatUint(uint64(v), 10) +} + +type Uint64 struct { + atomic.Uint64 +} + +func NewUint64(val uint64) *Uint64 { + i := &Uint64{} + i.Store(val) + return i +} + +func (i *Uint64) MarshalJSON() ([]byte, error) { + return json.Marshal(i.Load()) +} + +func (i *Uint64) UnmarshalJSON(b []byte) error { + var v uint64 + if err := json.Unmarshal(b, &v); err != nil { + return err + } + i.Store(v) + return nil +} + +func (i *Uint64) String() string { + v := i.Load() + return strconv.FormatUint(uint64(v), 10) +} + +type Uintptr struct { + atomic.Uintptr +} + +func NewUintptr(val uintptr) *Uintptr { + i := &Uintptr{} + i.Store(val) + return i +} + +func (i *Uintptr) MarshalJSON() ([]byte, error) { + return json.Marshal(i.Load()) +} + +func (i *Uintptr) UnmarshalJSON(b []byte) error { + var v uintptr + if err := json.Unmarshal(b, &v); err != nil { + return err + } + i.Store(v) + return nil +} + +func (i *Uintptr) String() string { + v := i.Load() + return strconv.FormatUint(uint64(v), 10) +} diff --git a/common/atomic/value.go b/common/atomic/value.go new file mode 100644 index 00000000..ca0eb631 --- /dev/null +++ b/common/atomic/value.go @@ -0,0 +1,58 @@ +package atomic + +import ( + "encoding/json" + "sync/atomic" +) + +func DefaultValue[T any]() T { + var defaultValue T + return defaultValue +} + +type TypedValue[T any] struct { + value atomic.Value +} + +func (t *TypedValue[T]) Load() T { + value := t.value.Load() + if value == nil { + return DefaultValue[T]() + } + return value.(T) +} + +func (t *TypedValue[T]) Store(value T) { + t.value.Store(value) +} + +func (t *TypedValue[T]) Swap(new T) T { + old := t.value.Swap(new) + if old == nil { + return DefaultValue[T]() + } + return old.(T) +} + +func (t *TypedValue[T]) CompareAndSwap(old, new T) bool { + return t.value.CompareAndSwap(old, new) +} + +func (t *TypedValue[T]) MarshalJSON() ([]byte, error) { + return json.Marshal(t.Load()) +} + +func (t *TypedValue[T]) UnmarshalJSON(b []byte) error { + var v T + if err := json.Unmarshal(b, &v); err != nil { + return err + } + t.Store(v) + return nil +} + +func NewTypedValue[T any](t T) *TypedValue[T] { + v := &TypedValue[T]{} + v.Store(t) + return v +} diff --git a/common/observable/observable_test.go b/common/observable/observable_test.go index 5459e0e2..5a02273d 100644 --- a/common/observable/observable_test.go +++ b/common/observable/observable_test.go @@ -5,8 +5,9 @@ import ( "testing" "time" + "github.com/Dreamacro/clash/common/atomic" + "github.com/stretchr/testify/assert" - "go.uber.org/atomic" ) func iterator[T any](item []T) chan T { @@ -44,7 +45,7 @@ func TestObservable_MultiSubscribe(t *testing.T) { wg.Add(2) waitCh := func(ch <-chan int) { for range ch { - count.Inc() + count.Add(1) } wg.Done() } diff --git a/common/singledo/singledo_test.go b/common/singledo/singledo_test.go index 26e3d37d..9e114fb7 100644 --- a/common/singledo/singledo_test.go +++ b/common/singledo/singledo_test.go @@ -5,8 +5,9 @@ import ( "testing" "time" + "github.com/Dreamacro/clash/common/atomic" + "github.com/stretchr/testify/assert" - "go.uber.org/atomic" ) func TestBasic(t *testing.T) { @@ -26,7 +27,7 @@ func TestBasic(t *testing.T) { go func() { _, _, shard := single.Do(call) if shard { - shardCount.Inc() + shardCount.Add(1) } wg.Done() }() diff --git a/component/dialer/options.go b/component/dialer/options.go index 372a2e63..096c7a5c 100644 --- a/component/dialer/options.go +++ b/component/dialer/options.go @@ -4,14 +4,13 @@ import ( "context" "net" + "github.com/Dreamacro/clash/common/atomic" "github.com/Dreamacro/clash/component/resolver" - - "go.uber.org/atomic" ) var ( DefaultOptions []Option - DefaultInterface = atomic.NewString("") + DefaultInterface = atomic.NewTypedValue[string]("") DefaultRoutingMark = atomic.NewInt32(0) ) diff --git a/component/profile/profile.go b/component/profile/profile.go index e3d9e78c..aa6df2f7 100644 --- a/component/profile/profile.go +++ b/component/profile/profile.go @@ -1,7 +1,7 @@ package profile import ( - "go.uber.org/atomic" + "github.com/Dreamacro/clash/common/atomic" ) // StoreSelected is a global switch for storing selected proxy to cache diff --git a/dns/client.go b/dns/client.go index 637207f3..ba83412b 100644 --- a/dns/client.go +++ b/dns/client.go @@ -8,6 +8,7 @@ import ( "net/netip" "strings" + "github.com/Dreamacro/clash/common/atomic" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/resolver" tlsC "github.com/Dreamacro/clash/component/tls" @@ -15,7 +16,6 @@ import ( D "github.com/miekg/dns" "github.com/zhangyunhao116/fastrand" - "go.uber.org/atomic" ) type client struct { @@ -23,7 +23,7 @@ type client struct { r *Resolver port string host string - iface *atomic.String + iface *atomic.TypedValue[string] proxyAdapter C.ProxyAdapter proxyName string addr string diff --git a/dns/dhcp.go b/dns/dhcp.go index 151e4421..a6c1df76 100644 --- a/dns/dhcp.go +++ b/dns/dhcp.go @@ -8,8 +8,7 @@ import ( "sync" "time" - "go.uber.org/atomic" - + "github.com/Dreamacro/clash/common/atomic" "github.com/Dreamacro/clash/component/dhcp" "github.com/Dreamacro/clash/component/iface" "github.com/Dreamacro/clash/component/resolver" @@ -86,7 +85,7 @@ func (d *dhcpClient) resolve(ctx context.Context) ([]dnsClient, error) { for _, item := range dns { nameserver = append(nameserver, NameServer{ Addr: net.JoinHostPort(item.String(), "53"), - Interface: atomic.NewString(d.ifaceName), + Interface: atomic.NewTypedValue(d.ifaceName), }) } diff --git a/dns/resolver.go b/dns/resolver.go index df8ed3d1..7e1b007d 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -7,8 +7,7 @@ import ( "strings" "time" - "go.uber.org/atomic" - + "github.com/Dreamacro/clash/common/atomic" "github.com/Dreamacro/clash/common/cache" "github.com/Dreamacro/clash/component/fakeip" "github.com/Dreamacro/clash/component/geodata/router" @@ -388,7 +387,7 @@ func (r *Resolver) Invalid() bool { type NameServer struct { Net string Addr string - Interface *atomic.String + Interface *atomic.TypedValue[string] ProxyAdapter C.ProxyAdapter ProxyName string Params map[string]string diff --git a/go.mod b/go.mod index ecc269df..1e3b7a1e 100644 --- a/go.mod +++ b/go.mod @@ -41,7 +41,6 @@ require ( github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837 github.com/zhangyunhao116/fastrand v0.3.0 go.etcd.io/bbolt v1.3.6 - go.uber.org/atomic v1.10.0 go.uber.org/automaxprocs v1.5.2 golang.org/x/crypto v0.8.0 golang.org/x/exp v0.0.0-20230321023759-10a507213a29 @@ -103,5 +102,3 @@ require ( golang.org/x/tools v0.6.0 // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect ) - -replace go.uber.org/atomic v1.10.0 => github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370 diff --git a/go.sum b/go.sum index a6a61afd..ec90cb29 100644 --- a/go.sum +++ b/go.sum @@ -102,8 +102,6 @@ github.com/metacubex/sing-tun v0.1.4-0.20230420054006-376d37578990 h1:VpC69WWsOy github.com/metacubex/sing-tun v0.1.4-0.20230420054006-376d37578990/go.mod h1:5CR2e0WQQiLSPd46Qaz3uhDRRik3QvvXiyMvlT3bXoc= github.com/metacubex/sing-wireguard v0.0.0-20230420053659-05c12d25b6eb h1:uoeOqHaQmNPev9RewQlff9Z55yuczEhRizcgDf4lSAw= github.com/metacubex/sing-wireguard v0.0.0-20230420053659-05c12d25b6eb/go.mod h1:Bsw2BvKMMMY0FhZPseDI50ZOalvoUPMKYyGpyqvIIqY= -github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370 h1:UkViS4DCESAUEYgbIEQdD02hyMacFt6Dny+1MOJtNIo= -github.com/metacubex/uber-atomic v0.0.0-20230202125923-feb10b770370/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw= github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/mroth/weightedrand/v2 v2.0.0 h1:ADehnByWbliEDIazDAKFdBHoqgHSXAkgyKqM/9YsPoo= diff --git a/transport/gun/gun.go b/transport/gun/gun.go index 0e5d2321..36cf68f8 100644 --- a/transport/gun/gun.go +++ b/transport/gun/gun.go @@ -17,11 +17,11 @@ import ( "sync" "time" + "github.com/Dreamacro/clash/common/atomic" "github.com/Dreamacro/clash/common/buf" "github.com/Dreamacro/clash/common/pool" tlsC "github.com/Dreamacro/clash/component/tls" - "go.uber.org/atomic" "golang.org/x/net/http2" ) diff --git a/tunnel/statistic/manager.go b/tunnel/statistic/manager.go index 66e3857d..381ea7e6 100644 --- a/tunnel/statistic/manager.go +++ b/tunnel/statistic/manager.go @@ -5,8 +5,9 @@ import ( "sync" "time" + "github.com/Dreamacro/clash/common/atomic" + "github.com/shirou/gopsutil/v3/process" - "go.uber.org/atomic" ) var DefaultManager *Manager diff --git a/tunnel/statistic/tracker.go b/tunnel/statistic/tracker.go index 96376eb4..b9682c22 100644 --- a/tunnel/statistic/tracker.go +++ b/tunnel/statistic/tracker.go @@ -4,12 +4,12 @@ import ( "net" "time" + "github.com/Dreamacro/clash/common/atomic" "github.com/Dreamacro/clash/common/buf" "github.com/Dreamacro/clash/common/utils" C "github.com/Dreamacro/clash/constant" "github.com/gofrs/uuid/v5" - "go.uber.org/atomic" ) type tracker interface { From aa6fa7f1e3299c7a0a447dbba531ef0d771b0785 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 22 Apr 2023 19:17:45 +0800 Subject: [PATCH 73/88] chore: cleanup unneeded deadline --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 1e3b7a1e..a8272799 100644 --- a/go.mod +++ b/go.mod @@ -20,15 +20,15 @@ require ( github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/mdlayher/netlink v1.7.2-0.20221213171556-9881fafed8c7 github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 - github.com/metacubex/sing-shadowsocks v0.2.2-0.20230419154412-11ec6808f588 - github.com/metacubex/sing-tun v0.1.4-0.20230420054006-376d37578990 + github.com/metacubex/sing-shadowsocks v0.2.2-0.20230422111054-f54786eee8ba + github.com/metacubex/sing-tun v0.1.4 github.com/metacubex/sing-wireguard v0.0.0-20230420053659-05c12d25b6eb github.com/miekg/dns v1.1.53 github.com/mroth/weightedrand/v2 v2.0.0 github.com/openacid/low v0.1.21 github.com/oschwald/geoip2-golang v1.8.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 - github.com/sagernet/sing v0.2.4-0.20230420044236-72471d9b35b5 + github.com/sagernet/sing v0.2.4 github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 diff --git a/go.sum b/go.sum index ec90cb29..476c8c3a 100644 --- a/go.sum +++ b/go.sum @@ -96,10 +96,10 @@ github.com/metacubex/gvisor v0.0.0-20230417114019-3c3ee672d60c h1:D62872jiuzC6b+ github.com/metacubex/gvisor v0.0.0-20230417114019-3c3ee672d60c/go.mod h1:wqEuzdImyqD2MCGE8CYRJXbB77oSEJeoSSXXdwKjnsE= github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 h1:KD96JPdTIayTGGgRl6PuVqo2Bpo6+x3LqDDyqrYDDXw= github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594/go.mod h1:9nOiGX6kqV3+ZbkDKdTNzdFD726QQHPH6WDb36jUSpA= -github.com/metacubex/sing-shadowsocks v0.2.2-0.20230419154412-11ec6808f588 h1:bOuVbeG5iwYyiDB2am3xIrt7GwbZC8cZIoJqLtvkcfY= -github.com/metacubex/sing-shadowsocks v0.2.2-0.20230419154412-11ec6808f588/go.mod h1:4uQQReKMTU7KTfOykVBe/oGJ00pl38d+BYJ99+mx26s= -github.com/metacubex/sing-tun v0.1.4-0.20230420054006-376d37578990 h1:VpC69WWsOyhwwRkMVop6ao30dh4ZtlqaroT5uK6EBYU= -github.com/metacubex/sing-tun v0.1.4-0.20230420054006-376d37578990/go.mod h1:5CR2e0WQQiLSPd46Qaz3uhDRRik3QvvXiyMvlT3bXoc= +github.com/metacubex/sing-shadowsocks v0.2.2-0.20230422111054-f54786eee8ba h1:He8YwyK600lHAS1xxNsP4k/jnZ8zqQ34XjCGn925+Yk= +github.com/metacubex/sing-shadowsocks v0.2.2-0.20230422111054-f54786eee8ba/go.mod h1:4uQQReKMTU7KTfOykVBe/oGJ00pl38d+BYJ99+mx26s= +github.com/metacubex/sing-tun v0.1.4 h1:OQDBNHjuPKrOprCiK+sLt97YQ0K6b9ZWmJB6z51ibZQ= +github.com/metacubex/sing-tun v0.1.4/go.mod h1:BMfG00enVf90/CzcdX9PK3Dymgl7BZqHXJfexEyB7Cc= github.com/metacubex/sing-wireguard v0.0.0-20230420053659-05c12d25b6eb h1:uoeOqHaQmNPev9RewQlff9Z55yuczEhRizcgDf4lSAw= github.com/metacubex/sing-wireguard v0.0.0-20230420053659-05c12d25b6eb/go.mod h1:Bsw2BvKMMMY0FhZPseDI50ZOalvoUPMKYyGpyqvIIqY= github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw= @@ -142,8 +142,8 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= -github.com/sagernet/sing v0.2.4-0.20230420044236-72471d9b35b5 h1:UtihSORXefZYQrGIssu65bP18VEgMGCNHdy60VHc14M= -github.com/sagernet/sing v0.2.4-0.20230420044236-72471d9b35b5/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= +github.com/sagernet/sing v0.2.4 h1:gC8BR5sglbJZX23RtMyFa8EETP9YEUADhfbEzU1yVbo= +github.com/sagernet/sing v0.2.4/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b h1:ouW/6IDCrxkBe19YSbdCd7buHix7b+UZ6BM4Zz74XF4= github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b/go.mod h1:oG8bPerYI6cZ74KquY3DvA7ynECyrILPBnce6wtBqeI= github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 h1:BHOnxrbC929JonuKqFdJ7ZbDp7zs4oTlH5KFvKtWu9U= From 7ca4b64a2b21b01eea04d2162b698080bb9c87b9 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 23 Apr 2023 19:57:54 +0800 Subject: [PATCH 74/88] feat: add proxy and sing-based listener support sing-mux --- adapter/outbound/singmux.go | 97 +++++++++++++++++++++++++++++++++++++ adapter/parser.go | 14 ++++++ go.mod | 5 +- go.sum | 12 ++++- listener/sing/sing.go | 10 ++++ 5 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 adapter/outbound/singmux.go diff --git a/adapter/outbound/singmux.go b/adapter/outbound/singmux.go new file mode 100644 index 00000000..315b53c6 --- /dev/null +++ b/adapter/outbound/singmux.go @@ -0,0 +1,97 @@ +package outbound + +import ( + "context" + "net" + + "github.com/Dreamacro/clash/component/dialer" + "github.com/Dreamacro/clash/component/proxydialer" + C "github.com/Dreamacro/clash/constant" + + mux "github.com/sagernet/sing-mux" + E "github.com/sagernet/sing/common/exceptions" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" +) + +type SingMux struct { + C.ProxyAdapter + base ProxyBase + client *mux.Client + dialer *muxSingDialer +} + +type SingMuxOption struct { + Enabled bool `proxy:"enabled,omitempty"` + Protocol string `proxy:"protocol,omitempty"` + MaxConnections int `proxy:"max-connections,omitempty"` + MinStreams int `proxy:"min-streams,omitempty"` + MaxStreams int `proxy:"max-streams,omitempty"` + Padding bool `proxy:"padding,omitempty"` +} + +type ProxyBase interface { + DialOptions(opts ...dialer.Option) []dialer.Option +} + +type muxSingDialer struct { + dialer dialer.Dialer + proxy C.ProxyAdapter +} + +var _ N.Dialer = (*muxSingDialer)(nil) + +func (d *muxSingDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { + var cDialer C.Dialer = proxydialer.New(d.proxy, d.dialer, false) + return cDialer.DialContext(ctx, network, destination.String()) +} + +func (d *muxSingDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { + var cDialer C.Dialer = proxydialer.New(d.proxy, d.dialer, false) + return cDialer.ListenPacket(ctx, "udp", "", destination.AddrPort()) +} + +func (s *SingMux) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { + options := s.base.DialOptions(opts...) + s.dialer.dialer = dialer.NewDialer(options...) + c, err := s.client.DialContext(ctx, "tcp", M.ParseSocksaddr(metadata.RemoteAddress())) + if err != nil { + return nil, err + } + return NewConn(c, s.ProxyAdapter), err +} + +func (s *SingMux) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { + options := s.base.DialOptions(opts...) + s.dialer.dialer = dialer.NewDialer(options...) + pc, err := s.client.ListenPacket(ctx, M.ParseSocksaddr(metadata.RemoteAddress())) + if err != nil { + return nil, err + } + if pc == nil { + return nil, E.New("packetConn is nil") + } + return newPacketConn(pc, s.ProxyAdapter), nil +} + +func NewSingMux(option SingMuxOption, proxy C.ProxyAdapter, base ProxyBase) (C.ProxyAdapter, error) { + singDialer := &muxSingDialer{dialer: dialer.NewDialer(), proxy: proxy} + client, err := mux.NewClient(mux.Options{ + Context: context.TODO(), + Dialer: singDialer, + Protocol: option.Protocol, + MaxConnections: option.MaxConnections, + MinStreams: option.MinStreams, + MaxStreams: option.MaxStreams, + Padding: option.Padding, + }) + if err != nil { + return nil, err + } + return &SingMux{ + ProxyAdapter: proxy, + base: base, + client: client, + dialer: singDialer, + }, nil +} diff --git a/adapter/parser.go b/adapter/parser.go index 1f626f33..a561a1ed 100644 --- a/adapter/parser.go +++ b/adapter/parser.go @@ -114,5 +114,19 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) { return nil, err } + if muxMapping, muxExist := mapping["smux"].(map[string]any); muxExist { + muxOption := &outbound.SingMuxOption{} + err = decoder.Decode(muxMapping, muxOption) + if err != nil { + return nil, err + } + if muxOption.Enabled { + proxy, err = outbound.NewSingMux(*muxOption, proxy, proxy.(outbound.ProxyBase)) + if err != nil { + return nil, err + } + } + } + return NewProxy(proxy), nil } diff --git a/go.mod b/go.mod index a8272799..4b2e0a43 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,8 @@ require ( github.com/openacid/low v0.1.21 github.com/oschwald/geoip2-golang v1.8.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 - github.com/sagernet/sing v0.2.4 + github.com/sagernet/sing v0.2.5-0.20230423085534-0902e6216207 + github.com/sagernet/sing-mux v0.0.0-20230423111236-a3ebc1453fd6 github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 @@ -69,6 +70,7 @@ require ( github.com/google/btree v1.0.1 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect + github.com/hashicorp/yamux v0.1.1 // indirect github.com/josharian/native v1.1.0 // indirect github.com/klauspost/compress v1.15.15 // indirect github.com/klauspost/cpuid/v2 v2.0.12 // indirect @@ -86,6 +88,7 @@ require ( github.com/quic-go/qtls-go1-19 v0.2.1 // indirect github.com/quic-go/qtls-go1-20 v0.1.1 // indirect github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect + github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect github.com/shoenig/go-m1cpu v0.1.5 // indirect github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect diff --git a/go.sum b/go.sum index 476c8c3a..26aa3830 100644 --- a/go.sum +++ b/go.sum @@ -67,6 +67,8 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= +github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16 h1:+aAGyK41KRn8jbF2Q7PLL0Sxwg6dShGcQSeCC7nZQ8E= github.com/insomniacslk/dhcp v0.0.0-20230407062729-974c6f05fe16/go.mod h1:IKrnDWs3/Mqq5n0lI+RxA2sB7MvN/vbMBP3ehXg65UI= @@ -142,12 +144,17 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= -github.com/sagernet/sing v0.2.4 h1:gC8BR5sglbJZX23RtMyFa8EETP9YEUADhfbEzU1yVbo= -github.com/sagernet/sing v0.2.4/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= +github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= +github.com/sagernet/sing v0.2.5-0.20230423085534-0902e6216207 h1:+dDVjW20IT+e8maKryaDeRY2+RFmTFdrQeIzqE2WOss= +github.com/sagernet/sing v0.2.5-0.20230423085534-0902e6216207/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= +github.com/sagernet/sing-mux v0.0.0-20230423111236-a3ebc1453fd6 h1:Ehndd61kh3NL2nL8xtDQMZ89YIbC1wCyFMK2PxEBnls= +github.com/sagernet/sing-mux v0.0.0-20230423111236-a3ebc1453fd6/go.mod h1:pF+RnLvCAOhECrvauy6LYOpBakJ/vuaF1Wm4lPsWryI= github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b h1:ouW/6IDCrxkBe19YSbdCd7buHix7b+UZ6BM4Zz74XF4= github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b/go.mod h1:oG8bPerYI6cZ74KquY3DvA7ynECyrILPBnce6wtBqeI= github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 h1:BHOnxrbC929JonuKqFdJ7ZbDp7zs4oTlH5KFvKtWu9U= github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3/go.mod h1:yKrAr+dqZd64DxBXCHWrYicp+n4qbqO73mtwv3dck8U= +github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as= +github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0= github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 h1:2ItpW1nMNkPzmBTxV0/eClCklHrFSQMnUGcpUmJxVeE= github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9/go.mod h1:FUyTEc5ye5NjKnDTDMuiLF2M6T4BE6y6KZuax//UCEg= github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfIATJ8oQwBmpOZJuozQG7Vk88lL4= @@ -239,6 +246,7 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/listener/sing/sing.go b/listener/sing/sing.go index 2941f6ca..2ccdfe2d 100644 --- a/listener/sing/sing.go +++ b/listener/sing/sing.go @@ -15,6 +15,7 @@ import ( "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/transport/socks5" + mux "github.com/sagernet/sing-mux" vmess "github.com/sagernet/sing-vmess" "github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/bufio/deadline" @@ -56,6 +57,13 @@ func (c *waitCloseConn) Upstream() any { return c.ExtendedConn } +func UpstreamMetadata(metadata M.Metadata) M.Metadata { + return M.Metadata{ + Source: metadata.Source, + Destination: metadata.Destination, + } +} + func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { additions := h.Additions if ctxAdditions := getAdditions(ctx); len(ctxAdditions) > 0 { @@ -63,6 +71,8 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta additions = append(additions, ctxAdditions...) } switch metadata.Destination.Fqdn { + case mux.Destination.Fqdn: + return mux.HandleConnection(ctx, h, log.SingLogger, conn, UpstreamMetadata(metadata)) case vmess.MuxDestination.Fqdn: return vmess.HandleMuxConnection(ctx, conn, h) case uot.MagicAddress: From 0c146bd04cfa22ede8af9826dc5ba9677e46cf8f Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 23 Apr 2023 20:10:58 +0800 Subject: [PATCH 75/88] doc: update smux --- docs/config.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/config.yaml b/docs/config.yaml index fe4ee682..660976e7 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -302,6 +302,13 @@ proxies: # socks5 # UDP 则为双栈解析,获取结果中的第一个 IPv4 # ipv6-prefer 同 ipv4-prefer # 现有协议都支持此参数,TCP 效果仅在开启 tcp-concurrent 生效 + smux: + enabled: false + protocol: smux # smux/yamux/h2mux + # max-connections: 4 # Maximum connections. Conflict with max-streams. + # min-streams: 4 # Minimum multiplexed streams in a connection before opening a new connection. Conflict with max-streams. + # max-streams: 0 # Maximum multiplexed streams in a connection before opening a new connection. Conflict with max-connections and min-streams. + # padding: false # Enable padding. Requires sing-box server version 1.3-beta9 or later. - name: "ss2" type: ss From 0c61057551b567d463b60623ed0ab2d06a18260c Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 23 Apr 2023 20:55:42 +0800 Subject: [PATCH 76/88] feat: add `statistic` and `only-tcp` options for smux --- adapter/outbound/singmux.go | 24 ++++++++++++++++-------- docs/config.yaml | 2 ++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/adapter/outbound/singmux.go b/adapter/outbound/singmux.go index 315b53c6..4f3a531c 100644 --- a/adapter/outbound/singmux.go +++ b/adapter/outbound/singmux.go @@ -16,9 +16,10 @@ import ( type SingMux struct { C.ProxyAdapter - base ProxyBase - client *mux.Client - dialer *muxSingDialer + base ProxyBase + client *mux.Client + dialer *muxSingDialer + onlyTcp bool } type SingMuxOption struct { @@ -28,6 +29,8 @@ type SingMuxOption struct { MinStreams int `proxy:"min-streams,omitempty"` MaxStreams int `proxy:"max-streams,omitempty"` Padding bool `proxy:"padding,omitempty"` + Statistic bool `proxy:"statistic,omitempty"` + OnlyTcp bool `proxy:"only-tcp,omitempty"` } type ProxyBase interface { @@ -35,19 +38,20 @@ type ProxyBase interface { } type muxSingDialer struct { - dialer dialer.Dialer - proxy C.ProxyAdapter + dialer dialer.Dialer + proxy C.ProxyAdapter + statistic bool } var _ N.Dialer = (*muxSingDialer)(nil) func (d *muxSingDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { - var cDialer C.Dialer = proxydialer.New(d.proxy, d.dialer, false) + var cDialer C.Dialer = proxydialer.New(d.proxy, d.dialer, d.statistic) return cDialer.DialContext(ctx, network, destination.String()) } func (d *muxSingDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { - var cDialer C.Dialer = proxydialer.New(d.proxy, d.dialer, false) + var cDialer C.Dialer = proxydialer.New(d.proxy, d.dialer, d.statistic) return cDialer.ListenPacket(ctx, "udp", "", destination.AddrPort()) } @@ -62,6 +66,9 @@ func (s *SingMux) DialContext(ctx context.Context, metadata *C.Metadata, opts .. } func (s *SingMux) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { + if s.onlyTcp { + return s.ProxyAdapter.ListenPacketContext(ctx, metadata, opts...) + } options := s.base.DialOptions(opts...) s.dialer.dialer = dialer.NewDialer(options...) pc, err := s.client.ListenPacket(ctx, M.ParseSocksaddr(metadata.RemoteAddress())) @@ -75,7 +82,7 @@ func (s *SingMux) ListenPacketContext(ctx context.Context, metadata *C.Metadata, } func NewSingMux(option SingMuxOption, proxy C.ProxyAdapter, base ProxyBase) (C.ProxyAdapter, error) { - singDialer := &muxSingDialer{dialer: dialer.NewDialer(), proxy: proxy} + singDialer := &muxSingDialer{dialer: dialer.NewDialer(), proxy: proxy, statistic: option.Statistic} client, err := mux.NewClient(mux.Options{ Context: context.TODO(), Dialer: singDialer, @@ -93,5 +100,6 @@ func NewSingMux(option SingMuxOption, proxy C.ProxyAdapter, base ProxyBase) (C.P base: base, client: client, dialer: singDialer, + onlyTcp: option.OnlyTcp, }, nil } diff --git a/docs/config.yaml b/docs/config.yaml index 660976e7..e0fe5577 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -309,6 +309,8 @@ proxies: # socks5 # min-streams: 4 # Minimum multiplexed streams in a connection before opening a new connection. Conflict with max-streams. # max-streams: 0 # Maximum multiplexed streams in a connection before opening a new connection. Conflict with max-connections and min-streams. # padding: false # Enable padding. Requires sing-box server version 1.3-beta9 or later. + # statistic: false # 控制是否将底层连接显示在面板中,方面打断底层连接 + # only-tcp: false # 如果设置为true, smux的设置将不会对udp生效,udp连接会直接走底层协议 - name: "ss2" type: ss From 7f1b7e7521d3cdab5cc971466c36144d1c6e9b4c Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 23 Apr 2023 21:50:42 +0800 Subject: [PATCH 77/88] fix: smux should show its support udp and uot --- adapter/outbound/singmux.go | 14 ++++++++++++++ adapter/outboundgroup/relay.go | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/adapter/outbound/singmux.go b/adapter/outbound/singmux.go index 4f3a531c..b1a64d99 100644 --- a/adapter/outbound/singmux.go +++ b/adapter/outbound/singmux.go @@ -81,6 +81,20 @@ func (s *SingMux) ListenPacketContext(ctx context.Context, metadata *C.Metadata, return newPacketConn(pc, s.ProxyAdapter), nil } +func (s *SingMux) SupportUDP() bool { + if s.onlyTcp { + return s.ProxyAdapter.SupportUOT() + } + return true +} + +func (s *SingMux) SupportUOT() bool { + if s.onlyTcp { + return s.ProxyAdapter.SupportUOT() + } + return true +} + func NewSingMux(option SingMuxOption, proxy C.ProxyAdapter, base ProxyBase) (C.ProxyAdapter, error) { singDialer := &muxSingDialer{dialer: dialer.NewDialer(), proxy: proxy, statistic: option.Statistic} client, err := mux.NewClient(mux.Options{ diff --git a/adapter/outboundgroup/relay.go b/adapter/outboundgroup/relay.go index a596454f..ba733616 100644 --- a/adapter/outboundgroup/relay.go +++ b/adapter/outboundgroup/relay.go @@ -92,7 +92,7 @@ func (r *Relay) SupportUDP() bool { switch proxy.SupportWithDialer() { case C.ALLNet: case C.UDP: - default: // C.TCP and C.NONet + default: // C.TCP and C.InvalidNet return false } } From 72a67ac5349b5f15842948943a1ec11703ac1f62 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 24 Apr 2023 08:07:17 +0800 Subject: [PATCH 78/88] chore: force set SelectAble when start load cache --- adapter/outboundgroup/fallback.go | 4 ++++ adapter/outboundgroup/selector.go | 4 ++++ adapter/outboundgroup/urltest.go | 4 ++++ adapter/outboundgroup/util.go | 1 + hub/executor/executor.go | 2 +- 5 files changed, 14 insertions(+), 1 deletion(-) diff --git a/adapter/outboundgroup/fallback.go b/adapter/outboundgroup/fallback.go index c79d9871..1f4e1580 100644 --- a/adapter/outboundgroup/fallback.go +++ b/adapter/outboundgroup/fallback.go @@ -138,6 +138,10 @@ func (f *Fallback) Set(name string) error { return nil } +func (f *Fallback) ForceSet(name string) { + f.selected = name +} + func NewFallback(option *GroupCommonOption, providers []provider.ProxyProvider) *Fallback { return &Fallback{ GroupBase: NewGroupBase(GroupBaseOption{ diff --git a/adapter/outboundgroup/selector.go b/adapter/outboundgroup/selector.go index b5b1cfa3..96934f0c 100644 --- a/adapter/outboundgroup/selector.go +++ b/adapter/outboundgroup/selector.go @@ -78,6 +78,10 @@ func (s *Selector) Set(name string) error { return errors.New("proxy not exist") } +func (s *Selector) ForceSet(name string) { + s.selected = name +} + // Unwrap implements C.ProxyAdapter func (s *Selector) Unwrap(metadata *C.Metadata, touch bool) C.Proxy { return s.selectedProxy(touch) diff --git a/adapter/outboundgroup/urltest.go b/adapter/outboundgroup/urltest.go index 6f10f78b..5b0d2a17 100644 --- a/adapter/outboundgroup/urltest.go +++ b/adapter/outboundgroup/urltest.go @@ -53,6 +53,10 @@ func (u *URLTest) Set(name string) error { return nil } +func (u *URLTest) ForceSet(name string) { + u.selected = name +} + // DialContext implements C.ProxyAdapter func (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (c C.Conn, err error) { proxy := u.fast(true) diff --git a/adapter/outboundgroup/util.go b/adapter/outboundgroup/util.go index adcda1aa..85373a1f 100644 --- a/adapter/outboundgroup/util.go +++ b/adapter/outboundgroup/util.go @@ -14,4 +14,5 @@ func tcpKeepAlive(c net.Conn) { type SelectAble interface { Set(string) error + ForceSet(name string) } diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 3598d873..724150e7 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -413,7 +413,7 @@ func patchSelectGroup(proxies map[string]C.Proxy) { continue } - selector.Set(selected) + selector.ForceSet(selected) } } From efcb278f618c3e771fecb4b2ef14023fb5e8853f Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 24 Apr 2023 10:30:12 +0800 Subject: [PATCH 79/88] chore: safe sing-mux close --- adapter/outbound/singmux.go | 17 ++++++++++++----- docs/config.yaml | 2 +- go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/adapter/outbound/singmux.go b/adapter/outbound/singmux.go index b1a64d99..cf18a6f3 100644 --- a/adapter/outbound/singmux.go +++ b/adapter/outbound/singmux.go @@ -3,7 +3,9 @@ package outbound import ( "context" "net" + "runtime" + CN "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/proxydialer" C "github.com/Dreamacro/clash/constant" @@ -62,7 +64,7 @@ func (s *SingMux) DialContext(ctx context.Context, metadata *C.Metadata, opts .. if err != nil { return nil, err } - return NewConn(c, s.ProxyAdapter), err + return NewConn(CN.NewRefConn(c, s), s.ProxyAdapter), err } func (s *SingMux) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { @@ -78,7 +80,7 @@ func (s *SingMux) ListenPacketContext(ctx context.Context, metadata *C.Metadata, if pc == nil { return nil, E.New("packetConn is nil") } - return newPacketConn(pc, s.ProxyAdapter), nil + return newPacketConn(CN.NewRefPacketConn(pc, s), s.ProxyAdapter), nil } func (s *SingMux) SupportUDP() bool { @@ -95,10 +97,13 @@ func (s *SingMux) SupportUOT() bool { return true } +func closeSingMux(s *SingMux) { + _ = s.client.Close() +} + func NewSingMux(option SingMuxOption, proxy C.ProxyAdapter, base ProxyBase) (C.ProxyAdapter, error) { singDialer := &muxSingDialer{dialer: dialer.NewDialer(), proxy: proxy, statistic: option.Statistic} client, err := mux.NewClient(mux.Options{ - Context: context.TODO(), Dialer: singDialer, Protocol: option.Protocol, MaxConnections: option.MaxConnections, @@ -109,11 +114,13 @@ func NewSingMux(option SingMuxOption, proxy C.ProxyAdapter, base ProxyBase) (C.P if err != nil { return nil, err } - return &SingMux{ + outbound := &SingMux{ ProxyAdapter: proxy, base: base, client: client, dialer: singDialer, onlyTcp: option.OnlyTcp, - }, nil + } + runtime.SetFinalizer(outbound, closeSingMux) + return outbound, nil } diff --git a/docs/config.yaml b/docs/config.yaml index e0fe5577..97bd40ec 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -309,7 +309,7 @@ proxies: # socks5 # min-streams: 4 # Minimum multiplexed streams in a connection before opening a new connection. Conflict with max-streams. # max-streams: 0 # Maximum multiplexed streams in a connection before opening a new connection. Conflict with max-connections and min-streams. # padding: false # Enable padding. Requires sing-box server version 1.3-beta9 or later. - # statistic: false # 控制是否将底层连接显示在面板中,方面打断底层连接 + # statistic: false # 控制是否将底层连接显示在面板中,方便打断底层连接 # only-tcp: false # 如果设置为true, smux的设置将不会对udp生效,udp连接会直接走底层协议 - name: "ss2" diff --git a/go.mod b/go.mod index 4b2e0a43..4de1206f 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,7 @@ require ( github.com/oschwald/geoip2-golang v1.8.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 github.com/sagernet/sing v0.2.5-0.20230423085534-0902e6216207 - github.com/sagernet/sing-mux v0.0.0-20230423111236-a3ebc1453fd6 + github.com/sagernet/sing-mux v0.0.0-20230424015424-9b0d527c3bb0 github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 diff --git a/go.sum b/go.sum index 26aa3830..e8573a63 100644 --- a/go.sum +++ b/go.sum @@ -147,8 +147,8 @@ github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2 github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= github.com/sagernet/sing v0.2.5-0.20230423085534-0902e6216207 h1:+dDVjW20IT+e8maKryaDeRY2+RFmTFdrQeIzqE2WOss= github.com/sagernet/sing v0.2.5-0.20230423085534-0902e6216207/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= -github.com/sagernet/sing-mux v0.0.0-20230423111236-a3ebc1453fd6 h1:Ehndd61kh3NL2nL8xtDQMZ89YIbC1wCyFMK2PxEBnls= -github.com/sagernet/sing-mux v0.0.0-20230423111236-a3ebc1453fd6/go.mod h1:pF+RnLvCAOhECrvauy6LYOpBakJ/vuaF1Wm4lPsWryI= +github.com/sagernet/sing-mux v0.0.0-20230424015424-9b0d527c3bb0 h1:87jyxzTjq01VgEiUVSMNRKjCfsSfp/QwyUVT37eXY50= +github.com/sagernet/sing-mux v0.0.0-20230424015424-9b0d527c3bb0/go.mod h1:pF+RnLvCAOhECrvauy6LYOpBakJ/vuaF1Wm4lPsWryI= github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b h1:ouW/6IDCrxkBe19YSbdCd7buHix7b+UZ6BM4Zz74XF4= github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b/go.mod h1:oG8bPerYI6cZ74KquY3DvA7ynECyrILPBnce6wtBqeI= github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 h1:BHOnxrbC929JonuKqFdJ7ZbDp7zs4oTlH5KFvKtWu9U= From 7bb5da30056d45ca7a465e23398053447f5610e0 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 25 Apr 2023 23:01:05 +0800 Subject: [PATCH 80/88] chore: support splice for direct outbound --- adapter/outbound/base.go | 8 ++++++++ common/buf/sing.go | 12 ++++++------ common/callback/callback.go | 8 ++++++++ common/net/bufconn.go | 10 ++++++++++ common/net/refconn.go | 8 ++++++++ common/net/sing.go | 5 +++++ component/dialer/tfo.go | 8 ++++++++ go.mod | 2 +- go.sum | 4 ++-- tunnel/statistic/tracker.go | 20 ++++++++++++++++++++ 10 files changed, 76 insertions(+), 9 deletions(-) diff --git a/adapter/outbound/base.go b/adapter/outbound/base.go index 04eec966..367638b8 100644 --- a/adapter/outbound/base.go +++ b/adapter/outbound/base.go @@ -204,6 +204,14 @@ func (c *conn) Upstream() any { return c.ExtendedConn } +func (c *conn) WriterReplaceable() bool { + return true +} + +func (c *conn) ReaderReplaceable() bool { + return true +} + func NewConn(c net.Conn, a C.ProxyAdapter) C.Conn { if _, ok := c.(syscall.Conn); !ok { // exclusion system conn like *net.TCPConn c = N.NewDeadlineConn(c) // most conn from outbound can't handle readDeadline correctly diff --git a/common/buf/sing.go b/common/buf/sing.go index c176ecb1..93140887 100644 --- a/common/buf/sing.go +++ b/common/buf/sing.go @@ -9,12 +9,12 @@ const BufferSize = buf.BufferSize type Buffer = buf.Buffer -var ( - New = buf.New - StackNew = buf.StackNew - StackNewSize = buf.StackNewSize - With = buf.With -) +var New = buf.New +var NewSize = buf.NewSize +var StackNew = buf.StackNew +var StackNewSize = buf.StackNewSize +var With = buf.With +var As = buf.As var KeepAlive = common.KeepAlive diff --git a/common/callback/callback.go b/common/callback/callback.go index 0bf720f4..fe76dc67 100644 --- a/common/callback/callback.go +++ b/common/callback/callback.go @@ -36,6 +36,14 @@ func (c *firstWriteCallBackConn) Upstream() any { return c.Conn } +func (c *firstWriteCallBackConn) WriterReplaceable() bool { + return c.written +} + +func (c *firstWriteCallBackConn) ReaderReplaceable() bool { + return true +} + var _ N.ExtendedConn = (*firstWriteCallBackConn)(nil) func NewFirstWriteCallBackConn(c C.Conn, callback func(error)) C.Conn { diff --git a/common/net/bufconn.go b/common/net/bufconn.go index f01a3dda..2ff73c82 100644 --- a/common/net/bufconn.go +++ b/common/net/bufconn.go @@ -69,6 +69,16 @@ func (c *BufferedConn) ReadBuffer(buffer *buf.Buffer) (err error) { return c.ExtendedConn.ReadBuffer(buffer) } +func (c *BufferedConn) ReadCached() *buf.Buffer { // call in sing/common/bufio.Copy + if c.r.Buffered() > 0 { + length := c.r.Buffered() + b, _ := c.r.Peek(length) + _, _ = c.r.Discard(length) + return buf.As(b) + } + return nil +} + func (c *BufferedConn) Upstream() any { return c.ExtendedConn } diff --git a/common/net/refconn.go b/common/net/refconn.go index 59225db0..537cb839 100644 --- a/common/net/refconn.go +++ b/common/net/refconn.go @@ -67,6 +67,14 @@ func (c *refConn) WriteBuffer(buffer *buf.Buffer) error { return c.conn.WriteBuffer(buffer) } +func (c *refConn) ReaderReplaceable() bool { // Relay() will handle reference + return true +} + +func (c *refConn) WriterReplaceable() bool { // Relay() will handle reference + return true +} + var _ ExtendedConn = (*refConn)(nil) func NewRefConn(conn net.Conn, ref any) net.Conn { diff --git a/common/net/sing.go b/common/net/sing.go index 89517326..7eb92f03 100644 --- a/common/net/sing.go +++ b/common/net/sing.go @@ -3,6 +3,7 @@ package net import ( "context" "net" + "runtime" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/bufio" @@ -33,7 +34,11 @@ func NeedHandshake(conn any) bool { return false } +type CountFunc = network.CountFunc + // Relay copies between left and right bidirectionally. func Relay(leftConn, rightConn net.Conn) { + defer runtime.KeepAlive(leftConn) + defer runtime.KeepAlive(rightConn) _ = bufio.CopyConn(context.TODO(), leftConn, rightConn) } diff --git a/component/dialer/tfo.go b/component/dialer/tfo.go index d5337bdb..09d5cced 100644 --- a/component/dialer/tfo.go +++ b/component/dialer/tfo.go @@ -113,6 +113,14 @@ func (c *tfoConn) NeedHandshake() bool { return c.Conn == nil } +func (c *tfoConn) ReaderReplaceable() bool { + return c.Conn != nil +} + +func (c *tfoConn) WriterReplaceable() bool { + return c.Conn != nil +} + func dialTFO(ctx context.Context, netDialer net.Dialer, network, address string) (net.Conn, error) { ctx, cancel := context.WithCancel(ctx) dialer := tfo.Dialer{Dialer: netDialer, DisableTFO: false} diff --git a/go.mod b/go.mod index 4de1206f..3153ab1d 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/openacid/low v0.1.21 github.com/oschwald/geoip2-golang v1.8.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 - github.com/sagernet/sing v0.2.5-0.20230423085534-0902e6216207 + github.com/sagernet/sing v0.2.5-0.20230425145913-0c037cb0e2c8 github.com/sagernet/sing-mux v0.0.0-20230424015424-9b0d527c3bb0 github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 diff --git a/go.sum b/go.sum index e8573a63..a747291f 100644 --- a/go.sum +++ b/go.sum @@ -145,8 +145,8 @@ github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6E github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= -github.com/sagernet/sing v0.2.5-0.20230423085534-0902e6216207 h1:+dDVjW20IT+e8maKryaDeRY2+RFmTFdrQeIzqE2WOss= -github.com/sagernet/sing v0.2.5-0.20230423085534-0902e6216207/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= +github.com/sagernet/sing v0.2.5-0.20230425145913-0c037cb0e2c8 h1:bf0WjE6g+bRhNCSe5u0xTeZLK/NboSy1/tI4eqPjvU0= +github.com/sagernet/sing v0.2.5-0.20230425145913-0c037cb0e2c8/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= github.com/sagernet/sing-mux v0.0.0-20230424015424-9b0d527c3bb0 h1:87jyxzTjq01VgEiUVSMNRKjCfsSfp/QwyUVT37eXY50= github.com/sagernet/sing-mux v0.0.0-20230424015424-9b0d527c3bb0/go.mod h1:pF+RnLvCAOhECrvauy6LYOpBakJ/vuaF1Wm4lPsWryI= github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b h1:ouW/6IDCrxkBe19YSbdCd7buHix7b+UZ6BM4Zz74XF4= diff --git a/tunnel/statistic/tracker.go b/tunnel/statistic/tracker.go index b9682c22..c4feeddb 100644 --- a/tunnel/statistic/tracker.go +++ b/tunnel/statistic/tracker.go @@ -1,11 +1,13 @@ package statistic import ( + "io" "net" "time" "github.com/Dreamacro/clash/common/atomic" "github.com/Dreamacro/clash/common/buf" + N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/utils" C "github.com/Dreamacro/clash/constant" @@ -61,6 +63,15 @@ func (tt *tcpTracker) ReadBuffer(buffer *buf.Buffer) (err error) { return } +func (tt *tcpTracker) UnwrapReader() (io.Reader, []N.CountFunc) { + return tt.Conn, []N.CountFunc{func(download int64) { + if tt.pushToManager { + tt.manager.PushDownloaded(download) + } + tt.DownloadTotal.Add(download) + }} +} + func (tt *tcpTracker) Write(b []byte) (int, error) { n, err := tt.Conn.Write(b) upload := int64(n) @@ -81,6 +92,15 @@ func (tt *tcpTracker) WriteBuffer(buffer *buf.Buffer) (err error) { return } +func (tt *tcpTracker) UnwrapWriter() (io.Writer, []N.CountFunc) { + return tt.Conn, []N.CountFunc{func(upload int64) { + if tt.pushToManager { + tt.manager.PushUploaded(upload) + } + tt.UploadTotal.Add(upload) + }} +} + func (tt *tcpTracker) Close() error { tt.manager.Leave(tt) return tt.Conn.Close() From 787e5ea28d0cc7b130a5c8a92c0dfa955e7ee913 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Wed, 26 Apr 2023 01:13:52 +0000 Subject: [PATCH 81/88] chore: version print error --- constant/features/low_memory.go | 1 + 1 file changed, 1 insertion(+) diff --git a/constant/features/low_memory.go b/constant/features/low_memory.go index 32d10fa6..0d252113 100644 --- a/constant/features/low_memory.go +++ b/constant/features/low_memory.go @@ -1,3 +1,4 @@ +//go:build with_low_memory package features func init() { From e1af1abcc23d76e184b3cdd37f7fc928d1b822d7 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 26 Apr 2023 09:33:58 +0800 Subject: [PATCH 82/88] fix: wireguard auto close not working --- adapter/outbound/wireguard.go | 169 ++++++++++++++++++++++++++++++++-- 1 file changed, 159 insertions(+), 10 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index f96f2d6f..67cd9092 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -40,6 +40,7 @@ type WireGuard struct { startOnce sync.Once startErr error resolver *dns.Resolver + refP *refProxyAdapter } type WireGuardOption struct { @@ -100,6 +101,20 @@ func (d *wgSingDialer) ListenPacket(ctx context.Context, destination M.Socksaddr return cDialer.ListenPacket(ctx, "udp", "", destination.AddrPort()) } +type wgSingErrorHandler struct { + name string +} + +var _ E.Handler = (*wgSingErrorHandler)(nil) + +func (w wgSingErrorHandler) NewError(ctx context.Context, err error) { + if E.IsClosedOrCanceled(err) { + log.SingLogger.Debug(fmt.Sprintf("[WG](%s) connection closed: %s", w.name, err)) + return + } + log.SingLogger.Error(fmt.Sprintf("[WG](%s) %s", w.name, err)) +} + type wgNetDialer struct { tunDevice wireguard.Device } @@ -174,7 +189,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { connectAddr = option.Addr() } } - outbound.bind = wireguard.NewClientBind(context.Background(), outbound, outbound.dialer, isConnect, connectAddr, reserved) + outbound.bind = wireguard.NewClientBind(context.Background(), wgSingErrorHandler{outbound.Name()}, outbound.dialer, isConnect, connectAddr, reserved) var localPrefixes []netip.Prefix @@ -312,13 +327,15 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { } } + refP := &refProxyAdapter{} + outbound.refP = refP if option.RemoteDnsResolve && len(option.Dns) > 0 { nss, err := dns.ParseNameServer(option.Dns) if err != nil { return nil, err } for i := range nss { - nss[i].ProxyAdapter = outbound + nss[i].ProxyAdapter = refP } outbound.resolver = dns.NewResolver(dns.Config{ Main: nss, @@ -329,14 +346,6 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { return outbound, nil } -func (w *WireGuard) NewError(ctx context.Context, err error) { - if E.IsClosedOrCanceled(err) { - log.SingLogger.Debug(fmt.Sprintf("[WG](%s) connection closed: %s", w.Name(), err)) - return - } - log.SingLogger.Error(fmt.Sprintf("[WG](%s) %s", w.Name(), err)) -} - func closeWireGuard(w *WireGuard) { if w.device != nil { w.device.Close() @@ -357,6 +366,8 @@ func (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata, opts if !metadata.Resolved() || w.resolver != nil { r := resolver.DefaultResolver if w.resolver != nil { + w.refP.SetProxyAdapter(w) + defer w.refP.ClearProxyAdapter() r = w.resolver } options = append(options, dialer.WithResolver(r)) @@ -391,6 +402,8 @@ func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadat if (!metadata.Resolved() || w.resolver != nil) && metadata.Host != "" { r := resolver.DefaultResolver if w.resolver != nil { + w.refP.SetProxyAdapter(w) + defer w.refP.ClearProxyAdapter() r = w.resolver } ip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) @@ -414,3 +427,139 @@ func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadat func (w *WireGuard) IsL3Protocol(metadata *C.Metadata) bool { return true } + +type refProxyAdapter struct { + proxyAdapter C.ProxyAdapter + count int + mutex sync.Mutex +} + +func (r *refProxyAdapter) SetProxyAdapter(proxyAdapter C.ProxyAdapter) { + r.mutex.Lock() + defer r.mutex.Unlock() + r.proxyAdapter = proxyAdapter + r.count++ +} + +func (r *refProxyAdapter) ClearProxyAdapter() { + r.mutex.Lock() + defer r.mutex.Unlock() + r.count-- + if r.count == 0 { + r.proxyAdapter = nil + } +} + +func (r *refProxyAdapter) Name() string { + if r.proxyAdapter != nil { + return r.proxyAdapter.Name() + } + return "" +} + +func (r *refProxyAdapter) Type() C.AdapterType { + if r.proxyAdapter != nil { + return r.proxyAdapter.Type() + } + return C.AdapterType(0) +} + +func (r *refProxyAdapter) Addr() string { + if r.proxyAdapter != nil { + return r.proxyAdapter.Addr() + } + return "" +} + +func (r *refProxyAdapter) SupportUDP() bool { + if r.proxyAdapter != nil { + return r.proxyAdapter.SupportUDP() + } + return false +} + +func (r *refProxyAdapter) SupportXUDP() bool { + if r.proxyAdapter != nil { + return r.proxyAdapter.SupportXUDP() + } + return false +} + +func (r *refProxyAdapter) SupportTFO() bool { + if r.proxyAdapter != nil { + return r.proxyAdapter.SupportTFO() + } + return false +} + +func (r *refProxyAdapter) MarshalJSON() ([]byte, error) { + if r.proxyAdapter != nil { + return r.proxyAdapter.MarshalJSON() + } + return nil, C.ErrNotSupport +} + +func (r *refProxyAdapter) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { + if r.proxyAdapter != nil { + return r.proxyAdapter.StreamConn(c, metadata) + } + return nil, C.ErrNotSupport +} + +func (r *refProxyAdapter) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { + if r.proxyAdapter != nil { + return r.proxyAdapter.DialContext(ctx, metadata, opts...) + } + return nil, C.ErrNotSupport +} + +func (r *refProxyAdapter) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { + if r.proxyAdapter != nil { + return r.proxyAdapter.ListenPacketContext(ctx, metadata, opts...) + } + return nil, C.ErrNotSupport +} + +func (r *refProxyAdapter) SupportUOT() bool { + if r.proxyAdapter != nil { + return r.proxyAdapter.SupportUOT() + } + return false +} + +func (r *refProxyAdapter) SupportWithDialer() C.NetWork { + if r.proxyAdapter != nil { + return r.proxyAdapter.SupportWithDialer() + } + return C.InvalidNet +} + +func (r *refProxyAdapter) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (C.Conn, error) { + if r.proxyAdapter != nil { + return r.proxyAdapter.DialContextWithDialer(ctx, dialer, metadata) + } + return nil, C.ErrNotSupport +} + +func (r *refProxyAdapter) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (C.PacketConn, error) { + if r.proxyAdapter != nil { + return r.proxyAdapter.ListenPacketWithDialer(ctx, dialer, metadata) + } + return nil, C.ErrNotSupport +} + +func (r *refProxyAdapter) IsL3Protocol(metadata *C.Metadata) bool { + if r.proxyAdapter != nil { + return r.proxyAdapter.IsL3Protocol(metadata) + } + return false +} + +func (r *refProxyAdapter) Unwrap(metadata *C.Metadata, touch bool) C.Proxy { + if r.proxyAdapter != nil { + return r.proxyAdapter.Unwrap(metadata, touch) + } + return nil +} + +var _ C.ProxyAdapter = (*refProxyAdapter)(nil) From d8db25ee89770f4055f3676086aee34352aea957 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Wed, 26 Apr 2023 02:49:16 +0000 Subject: [PATCH 83/88] fix: domain-set wildcard match --- component/trie/domain_set.go | 2 +- component/trie/domain_set_test.go | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/component/trie/domain_set.go b/component/trie/domain_set.go index ce416e16..be793ad3 100644 --- a/component/trie/domain_set.go +++ b/component/trie/domain_set.go @@ -105,7 +105,7 @@ func (ss *DomainSet) Has(key string) bool { } } for ; ; nextBmIdx++ { - if ss.labels[nextBmIdx-nextNodeId] == domainStepByte { + if nextBmIdx-nextNodeId < len(ss.labels) && ss.labels[nextBmIdx-nextNodeId] == domainStepByte { bmIdx = nextBmIdx nodeId = nextNodeId i = j diff --git a/component/trie/domain_set_test.go b/component/trie/domain_set_test.go index c4160f6c..9e0e0b70 100644 --- a/component/trie/domain_set_test.go +++ b/component/trie/domain_set_test.go @@ -65,6 +65,7 @@ func TestDomainSetWildcard(t *testing.T) { "stun.*.*", "*.*.qq.com", "test.*.baidu.com", + "*.apple.com", } for _, domain := range domainSet { @@ -78,6 +79,7 @@ func TestDomainSetWildcard(t *testing.T) { 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("a.www.google.com")) assert.False(t, set.Has("test.qq.com")) assert.False(t, set.Has("test.test.test.qq.com")) } From 8db030d36ed7443d20d65c66d6c25595aa00b71f Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 26 Apr 2023 11:05:45 +0800 Subject: [PATCH 84/88] fix: wireguard tcp close need long time --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3153ab1d..21064360 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/metacubex/quic-go v0.33.3-0.20230322045857-901b636b4594 github.com/metacubex/sing-shadowsocks v0.2.2-0.20230422111054-f54786eee8ba github.com/metacubex/sing-tun v0.1.4 - github.com/metacubex/sing-wireguard v0.0.0-20230420053659-05c12d25b6eb + github.com/metacubex/sing-wireguard v0.0.0-20230426030325-41db09ae771a github.com/miekg/dns v1.1.53 github.com/mroth/weightedrand/v2 v2.0.0 github.com/openacid/low v0.1.21 diff --git a/go.sum b/go.sum index a747291f..f26ac2ec 100644 --- a/go.sum +++ b/go.sum @@ -102,8 +102,8 @@ github.com/metacubex/sing-shadowsocks v0.2.2-0.20230422111054-f54786eee8ba h1:He github.com/metacubex/sing-shadowsocks v0.2.2-0.20230422111054-f54786eee8ba/go.mod h1:4uQQReKMTU7KTfOykVBe/oGJ00pl38d+BYJ99+mx26s= github.com/metacubex/sing-tun v0.1.4 h1:OQDBNHjuPKrOprCiK+sLt97YQ0K6b9ZWmJB6z51ibZQ= github.com/metacubex/sing-tun v0.1.4/go.mod h1:BMfG00enVf90/CzcdX9PK3Dymgl7BZqHXJfexEyB7Cc= -github.com/metacubex/sing-wireguard v0.0.0-20230420053659-05c12d25b6eb h1:uoeOqHaQmNPev9RewQlff9Z55yuczEhRizcgDf4lSAw= -github.com/metacubex/sing-wireguard v0.0.0-20230420053659-05c12d25b6eb/go.mod h1:Bsw2BvKMMMY0FhZPseDI50ZOalvoUPMKYyGpyqvIIqY= +github.com/metacubex/sing-wireguard v0.0.0-20230426030325-41db09ae771a h1:cWKym33Qvl6HA3hj4/YuYD8hHyqQPb47wT5cJRAPgco= +github.com/metacubex/sing-wireguard v0.0.0-20230426030325-41db09ae771a/go.mod h1:Bsw2BvKMMMY0FhZPseDI50ZOalvoUPMKYyGpyqvIIqY= github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw= github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/mroth/weightedrand/v2 v2.0.0 h1:ADehnByWbliEDIazDAKFdBHoqgHSXAkgyKqM/9YsPoo= From 888c233e9c51d4f7c80fac0566bf81f5615ef734 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 26 Apr 2023 13:33:35 +0800 Subject: [PATCH 85/88] fix: sing-mux udp --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 21064360..5e805c16 100644 --- a/go.mod +++ b/go.mod @@ -28,8 +28,8 @@ require ( github.com/openacid/low v0.1.21 github.com/oschwald/geoip2-golang v1.8.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 - github.com/sagernet/sing v0.2.5-0.20230425145913-0c037cb0e2c8 - github.com/sagernet/sing-mux v0.0.0-20230424015424-9b0d527c3bb0 + github.com/sagernet/sing v0.2.5-0.20230425211221-a23ffbaeb5b9 + github.com/sagernet/sing-mux v0.0.0-20230425130511-b0a6ffd8406f github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 github.com/sagernet/tfo-go v0.0.0-20230303015439-ffcfd8c41cf9 diff --git a/go.sum b/go.sum index f26ac2ec..5d251c05 100644 --- a/go.sum +++ b/go.sum @@ -145,10 +145,10 @@ github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6E github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= -github.com/sagernet/sing v0.2.5-0.20230425145913-0c037cb0e2c8 h1:bf0WjE6g+bRhNCSe5u0xTeZLK/NboSy1/tI4eqPjvU0= -github.com/sagernet/sing v0.2.5-0.20230425145913-0c037cb0e2c8/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= -github.com/sagernet/sing-mux v0.0.0-20230424015424-9b0d527c3bb0 h1:87jyxzTjq01VgEiUVSMNRKjCfsSfp/QwyUVT37eXY50= -github.com/sagernet/sing-mux v0.0.0-20230424015424-9b0d527c3bb0/go.mod h1:pF+RnLvCAOhECrvauy6LYOpBakJ/vuaF1Wm4lPsWryI= +github.com/sagernet/sing v0.2.5-0.20230425211221-a23ffbaeb5b9 h1:kpgKJbhesj6BBLTKIfBCJGQPm2ww7pNxn566C6TrHdA= +github.com/sagernet/sing v0.2.5-0.20230425211221-a23ffbaeb5b9/go.mod h1:Ta8nHnDLAwqySzKhGoKk4ZIB+vJ3GTKj7UPrWYvM+4w= +github.com/sagernet/sing-mux v0.0.0-20230425130511-b0a6ffd8406f h1:iEpOTgBTjt0vZJVXMTqYq13XyIu/337TWbq6WZ3CMWc= +github.com/sagernet/sing-mux v0.0.0-20230425130511-b0a6ffd8406f/go.mod h1:pF+RnLvCAOhECrvauy6LYOpBakJ/vuaF1Wm4lPsWryI= github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b h1:ouW/6IDCrxkBe19YSbdCd7buHix7b+UZ6BM4Zz74XF4= github.com/sagernet/sing-shadowtls v0.1.2-0.20230417103049-4f682e05f19b/go.mod h1:oG8bPerYI6cZ74KquY3DvA7ynECyrILPBnce6wtBqeI= github.com/sagernet/sing-vmess v0.1.5-0.20230417103030-8c3070ae3fb3 h1:BHOnxrbC929JonuKqFdJ7ZbDp7zs4oTlH5KFvKtWu9U= From eceea72a567c82cc222e1cff1f8ccfdcb5a45498 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 26 Apr 2023 15:57:25 +0800 Subject: [PATCH 86/88] fix: tunnel udp panic --- adapter/outbound/singmux.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/adapter/outbound/singmux.go b/adapter/outbound/singmux.go index cf18a6f3..acfdfe99 100644 --- a/adapter/outbound/singmux.go +++ b/adapter/outbound/singmux.go @@ -2,12 +2,14 @@ package outbound import ( "context" + "errors" "net" "runtime" CN "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/proxydialer" + "github.com/Dreamacro/clash/component/resolver" C "github.com/Dreamacro/clash/constant" mux "github.com/sagernet/sing-mux" @@ -73,7 +75,17 @@ func (s *SingMux) ListenPacketContext(ctx context.Context, metadata *C.Metadata, } options := s.base.DialOptions(opts...) s.dialer.dialer = dialer.NewDialer(options...) - pc, err := s.client.ListenPacket(ctx, M.ParseSocksaddr(metadata.RemoteAddress())) + + // sing-mux use stream-oriented udp with a special address, so we need a net.UDPAddr + if !metadata.Resolved() { + ip, err := resolver.ResolveIP(ctx, metadata.Host) + if err != nil { + return nil, errors.New("can't resolve ip") + } + metadata.DstIP = ip + } + + pc, err := s.client.ListenPacket(ctx, M.SocksaddrFromNet(metadata.UDPAddr())) if err != nil { return nil, err } From a1b6d6050f44646e0e32581263f197f90ad3f916 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Wed, 26 Apr 2023 14:02:21 +0000 Subject: [PATCH 87/88] chore: remove debug_api patch --- patch/add_debug_api.patch | 53 --------------------------------------- 1 file changed, 53 deletions(-) delete mode 100644 patch/add_debug_api.patch diff --git a/patch/add_debug_api.patch b/patch/add_debug_api.patch deleted file mode 100644 index 7134b378..00000000 --- a/patch/add_debug_api.patch +++ /dev/null @@ -1,53 +0,0 @@ -Subject: [PATCH] Chore: add debug api ---- -Index: hub/route/debug.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/hub/route/debug.go b/hub/route/debug.go -new file mode 100644 ---- /dev/null (revision df1007e2b14f7a526d176410995998bf06054657) -+++ b/hub/route/debug.go (revision df1007e2b14f7a526d176410995998bf06054657) -@@ -0,0 +1,21 @@ -+package route -+ -+import ( -+ "github.com/Dreamacro/clash/log" -+ "github.com/go-chi/chi/v5" -+ "github.com/go-chi/chi/v5/middleware" -+ "net/http" -+ "runtime" -+) -+ -+func debugRouter() http.Handler { -+ handler := middleware.Profiler() -+ r := chi.NewRouter() -+ r.Mount("/", handler) -+ r.Put("/gc", func(writer http.ResponseWriter, request *http.Request) { -+ log.Debugln("trigger GC") -+ runtime.GC() -+ }) -+ -+ return r -+} -Index: hub/route/server.go -IDEA additional info: -Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP -<+>UTF-8 -=================================================================== -diff --git a/hub/route/server.go b/hub/route/server.go ---- a/hub/route/server.go (revision f83fd6c690928ca7861196e3ca5af566303f95d5) -+++ b/hub/route/server.go (revision df1007e2b14f7a526d176410995998bf06054657) -@@ -59,6 +59,11 @@ - MaxAge: 300, - }) - r.Use(corsM.Handler) -+ -+ r.Group(func(r chi.Router) { -+ r.Mount("/debug", debugRouter()) -+ }) -+ - r.Group(func(r chi.Router) { - r.Use(authentication) - r.Get("/", hello) From 30f93debc4769319afb6bf77837e5a3557788fc8 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Wed, 26 Apr 2023 14:09:17 +0000 Subject: [PATCH 88/88] chore: better workflow --- .github/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 87cfb07f..86352a15 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,6 @@ on: - ".github/ISSUE_TEMPLATE/**" branches: - Alpha - - Meta tags: - "v*" pull_request_target: