From fbaa323ad4d730ca04b7325f44ca22a4c30abbff Mon Sep 17 00:00:00 2001
From: Larvan2 <78135608+Larvan2@users.noreply.github.com>
Date: Sat, 3 Jun 2023 18:25:00 +0800
Subject: [PATCH 01/34] chore: generate release note automatically
---
.github/genReleaseNote.sh | 32 ++++++++++++++++++++++++++++++++
.github/workflows/build.yml | 18 ++++++++++++++++++
2 files changed, 50 insertions(+)
create mode 100755 .github/genReleaseNote.sh
diff --git a/.github/genReleaseNote.sh b/.github/genReleaseNote.sh
new file mode 100755
index 00000000..0425061d
--- /dev/null
+++ b/.github/genReleaseNote.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+while getopts "v:" opt; do
+ case $opt in
+ v)
+ version_range=$OPTARG
+ ;;
+ \?)
+ echo "Invalid option: -$OPTARG" >&2
+ exit 1
+ ;;
+ esac
+done
+
+if [ -z "$version_range" ]; then
+ echo "Please provide the version range using -v option. Example: ./genReleashNote.sh -v v1.14.1...v1.14.2"
+ exit 1
+fi
+
+echo "## What's Changed" > release.md
+git log --pretty=format:"* %s by @%an" --grep="^feat" -i $version_range | sort -f | uniq >> release.md
+echo "" >> release.md
+
+echo "## BUG & Fix" >> release.md
+git log --pretty=format:"* %s by @%an" --grep="^fix" -i $version_range | sort -f | uniq >> release.md
+echo "" >> release.md
+
+echo "## Maintenance" >> release.md
+git log --pretty=format:"* %s by @%an" --grep="^chore\|^docs\|^refactor" -i $version_range | sort -f | uniq >> release.md
+echo "" >> release.md
+
+echo "**Full Changelog**: https://github.com/MetaCubeX/Clash.Meta/compare/$version_range" >> release.md
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index f9e04574..57c40a4f 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -288,6 +288,23 @@ jobs:
needs: [Build]
runs-on: ubuntu-latest
steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+
+ - name: Get tags
+ run: |
+ echo "CURRENTVERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
+ git fetch --tags
+ echo "PREVERSION=$(git describe --tags --abbrev=0 HEAD^)" >> $GITHUB_ENV
+
+ - name: Generate release notes
+ run: |
+ cp ./.github/genReleaseNote.sh ./
+ bash ./genReleaseNote.sh -v ${PREVERSION}...${CURRENTVERSION}
+ rm ./genReleaseNote.sh
+
- uses: actions/download-artifact@v3
with:
name: artifact
@@ -304,6 +321,7 @@ jobs:
tag_name: ${{ github.ref_name }}
files: bin/*
generate_release_notes: true
+ body_path: release.md
Docker:
if: ${{ !startsWith(github.event_name, 'pull_request') }}
From 0404e35be8736b695eae018a08debb175c1f96e6 Mon Sep 17 00:00:00 2001
From: Larvan2 <78135608+Larvan2@users.noreply.github.com>
Date: Tue, 2 Jan 2024 16:14:43 +0800
Subject: [PATCH 02/34] chore: update release note
---
.github/workflows/build.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 57c40a4f..6bd6dad0 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -268,6 +268,7 @@ jobs:
Synchronize ${{ github.ref_name }} branch code updates, keeping only the latest version
[我应该下载哪个文件? / Which file should I download?](https://github.com/MetaCubeX/mihomo/wiki/FAQ)
+ [二进制文件筛选 / Binary file selector] (https://metacubex.github.io/Meta-Docs/startup/#_1)
[查看文档 / Docs](https://metacubex.github.io/Meta-Docs/)
EOF
From 33bc7914e9725626b3bacdf351023d2af546d18b Mon Sep 17 00:00:00 2001
From: wwqgtxx
Date: Tue, 2 Jan 2024 18:26:45 +0800
Subject: [PATCH 03/34] chore: read waiter for pipe
---
common/net/deadline/pipe_sing.go | 217 +++++++++++++++++++++++++++++++
common/net/sing.go | 2 +
listener/http/client.go | 3 +-
listener/http/upgrade.go | 2 +-
listener/inner/tcp.go | 3 +-
transport/tuic/v4/client.go | 2 +-
transport/tuic/v5/client.go | 2 +-
tunnel/connection.go | 4 +-
tunnel/tunnel.go | 2 +-
9 files changed, 229 insertions(+), 8 deletions(-)
create mode 100644 common/net/deadline/pipe_sing.go
diff --git a/common/net/deadline/pipe_sing.go b/common/net/deadline/pipe_sing.go
new file mode 100644
index 00000000..20721fad
--- /dev/null
+++ b/common/net/deadline/pipe_sing.go
@@ -0,0 +1,217 @@
+package deadline
+
+import (
+ "io"
+ "net"
+ "os"
+ "sync"
+ "time"
+
+ "github.com/sagernet/sing/common/buf"
+ N "github.com/sagernet/sing/common/network"
+)
+
+type pipeAddr struct{}
+
+func (pipeAddr) Network() string { return "pipe" }
+func (pipeAddr) String() string { return "pipe" }
+
+type pipe struct {
+ wrMu sync.Mutex // Serialize Write operations
+
+ // Used by local Read to interact with remote Write.
+ // Successful receive on rdRx is always followed by send on rdTx.
+ rdRx <-chan []byte
+ rdTx chan<- int
+
+ // Used by local Write to interact with remote Read.
+ // Successful send on wrTx is always followed by receive on wrRx.
+ wrTx chan<- []byte
+ wrRx <-chan int
+
+ once sync.Once // Protects closing localDone
+ localDone chan struct{}
+ remoteDone <-chan struct{}
+
+ readDeadline pipeDeadline
+ writeDeadline pipeDeadline
+
+ readWaitOptions N.ReadWaitOptions
+}
+
+// Pipe creates a synchronous, in-memory, full duplex
+// network connection; both ends implement the Conn interface.
+// Reads on one end are matched with writes on the other,
+// copying data directly between the two; there is no internal
+// buffering.
+func Pipe() (net.Conn, net.Conn) {
+ cb1 := make(chan []byte)
+ cb2 := make(chan []byte)
+ cn1 := make(chan int)
+ cn2 := make(chan int)
+ done1 := make(chan struct{})
+ done2 := make(chan struct{})
+
+ p1 := &pipe{
+ rdRx: cb1, rdTx: cn1,
+ wrTx: cb2, wrRx: cn2,
+ localDone: done1, remoteDone: done2,
+ readDeadline: makePipeDeadline(),
+ writeDeadline: makePipeDeadline(),
+ }
+ p2 := &pipe{
+ rdRx: cb2, rdTx: cn2,
+ wrTx: cb1, wrRx: cn1,
+ localDone: done2, remoteDone: done1,
+ readDeadline: makePipeDeadline(),
+ writeDeadline: makePipeDeadline(),
+ }
+ return p1, p2
+}
+
+func (*pipe) LocalAddr() net.Addr { return pipeAddr{} }
+func (*pipe) RemoteAddr() net.Addr { return pipeAddr{} }
+
+func (p *pipe) Read(b []byte) (int, error) {
+ n, err := p.read(b)
+ if err != nil && err != io.EOF && err != io.ErrClosedPipe {
+ err = &net.OpError{Op: "read", Net: "pipe", Err: err}
+ }
+ return n, err
+}
+
+func (p *pipe) read(b []byte) (n int, err error) {
+ switch {
+ case isClosedChan(p.localDone):
+ return 0, io.ErrClosedPipe
+ case isClosedChan(p.remoteDone):
+ return 0, io.EOF
+ case isClosedChan(p.readDeadline.wait()):
+ return 0, os.ErrDeadlineExceeded
+ }
+
+ select {
+ case bw := <-p.rdRx:
+ nr := copy(b, bw)
+ p.rdTx <- nr
+ return nr, nil
+ case <-p.localDone:
+ return 0, io.ErrClosedPipe
+ case <-p.remoteDone:
+ return 0, io.EOF
+ case <-p.readDeadline.wait():
+ return 0, os.ErrDeadlineExceeded
+ }
+}
+
+func (p *pipe) Write(b []byte) (int, error) {
+ n, err := p.write(b)
+ if err != nil && err != io.ErrClosedPipe {
+ err = &net.OpError{Op: "write", Net: "pipe", Err: err}
+ }
+ return n, err
+}
+
+func (p *pipe) write(b []byte) (n int, err error) {
+ switch {
+ case isClosedChan(p.localDone):
+ return 0, io.ErrClosedPipe
+ case isClosedChan(p.remoteDone):
+ return 0, io.ErrClosedPipe
+ case isClosedChan(p.writeDeadline.wait()):
+ return 0, os.ErrDeadlineExceeded
+ }
+
+ p.wrMu.Lock() // Ensure entirety of b is written together
+ defer p.wrMu.Unlock()
+ for once := true; once || len(b) > 0; once = false {
+ select {
+ case p.wrTx <- b:
+ nw := <-p.wrRx
+ b = b[nw:]
+ n += nw
+ case <-p.localDone:
+ return n, io.ErrClosedPipe
+ case <-p.remoteDone:
+ return n, io.ErrClosedPipe
+ case <-p.writeDeadline.wait():
+ return n, os.ErrDeadlineExceeded
+ }
+ }
+ return n, nil
+}
+
+func (p *pipe) SetDeadline(t time.Time) error {
+ if isClosedChan(p.localDone) || isClosedChan(p.remoteDone) {
+ return io.ErrClosedPipe
+ }
+ p.readDeadline.set(t)
+ p.writeDeadline.set(t)
+ return nil
+}
+
+func (p *pipe) SetReadDeadline(t time.Time) error {
+ if isClosedChan(p.localDone) || isClosedChan(p.remoteDone) {
+ return io.ErrClosedPipe
+ }
+ p.readDeadline.set(t)
+ return nil
+}
+
+func (p *pipe) SetWriteDeadline(t time.Time) error {
+ if isClosedChan(p.localDone) || isClosedChan(p.remoteDone) {
+ return io.ErrClosedPipe
+ }
+ p.writeDeadline.set(t)
+ return nil
+}
+
+func (p *pipe) Close() error {
+ p.once.Do(func() { close(p.localDone) })
+ return nil
+}
+
+var _ N.ReadWaiter = (*pipe)(nil)
+
+func (p *pipe) InitializeReadWaiter(options N.ReadWaitOptions) (needCopy bool) {
+ p.readWaitOptions = options
+ return false
+}
+
+func (p *pipe) WaitReadBuffer() (buffer *buf.Buffer, err error) {
+ buffer, err = p.waitReadBuffer()
+ if err != nil && err != io.EOF && err != io.ErrClosedPipe {
+ err = &net.OpError{Op: "read", Net: "pipe", Err: err}
+ }
+ return
+}
+
+func (p *pipe) waitReadBuffer() (buffer *buf.Buffer, err error) {
+ switch {
+ case isClosedChan(p.localDone):
+ return nil, io.ErrClosedPipe
+ case isClosedChan(p.remoteDone):
+ return nil, io.EOF
+ case isClosedChan(p.readDeadline.wait()):
+ return nil, os.ErrDeadlineExceeded
+ }
+ select {
+ case bw := <-p.rdRx:
+ buffer = p.readWaitOptions.NewBuffer()
+ var nr int
+ nr, err = buffer.Write(bw)
+ if err != nil {
+ buffer.Release()
+ return
+ }
+ p.readWaitOptions.PostReturn(buffer)
+ p.rdTx <- nr
+ return
+ case <-p.localDone:
+ return nil, io.ErrClosedPipe
+ case <-p.remoteDone:
+ return nil, io.EOF
+ case <-p.readDeadline.wait():
+ return nil, os.ErrDeadlineExceeded
+ }
+}
diff --git a/common/net/sing.go b/common/net/sing.go
index f8698620..3296ad5b 100644
--- a/common/net/sing.go
+++ b/common/net/sing.go
@@ -35,6 +35,8 @@ func NeedHandshake(conn any) bool {
type CountFunc = network.CountFunc
+var Pipe = deadline.Pipe
+
// Relay copies between left and right bidirectionally.
func Relay(leftConn, rightConn net.Conn) {
defer runtime.KeepAlive(leftConn)
diff --git a/listener/http/client.go b/listener/http/client.go
index c35cadad..dfd1985f 100644
--- a/listener/http/client.go
+++ b/listener/http/client.go
@@ -8,6 +8,7 @@ import (
"time"
"github.com/metacubex/mihomo/adapter/inbound"
+ N "github.com/metacubex/mihomo/common/net"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/transport/socks5"
)
@@ -30,7 +31,7 @@ func newClient(srcConn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition)
return nil, socks5.ErrAddressNotSupported
}
- left, right := net.Pipe()
+ left, right := N.Pipe()
go tunnel.HandleTCPConn(inbound.NewHTTP(dstAddr, srcConn, right, additions...))
diff --git a/listener/http/upgrade.go b/listener/http/upgrade.go
index 8a6291d1..ac67ef68 100644
--- a/listener/http/upgrade.go
+++ b/listener/http/upgrade.go
@@ -41,7 +41,7 @@ func handleUpgrade(conn net.Conn, request *http.Request, tunnel C.Tunnel, additi
return
}
- left, right := net.Pipe()
+ left, right := N.Pipe()
go tunnel.HandleTCPConn(inbound.NewHTTP(dstAddr, conn, right, additions...))
diff --git a/listener/inner/tcp.go b/listener/inner/tcp.go
index 373fd2b4..dac3d721 100644
--- a/listener/inner/tcp.go
+++ b/listener/inner/tcp.go
@@ -6,6 +6,7 @@ import (
"net/netip"
"strconv"
+ N "github.com/metacubex/mihomo/common/net"
C "github.com/metacubex/mihomo/constant"
)
@@ -20,7 +21,7 @@ func HandleTcp(address string) (conn net.Conn, err error) {
return nil, errors.New("tcp uninitialized")
}
// executor Parsed
- conn1, conn2 := net.Pipe()
+ conn1, conn2 := N.Pipe()
metadata := &C.Metadata{}
metadata.NetWork = C.TCP
diff --git a/transport/tuic/v4/client.go b/transport/tuic/v4/client.go
index 67906959..5c9c889c 100644
--- a/transport/tuic/v4/client.go
+++ b/transport/tuic/v4/client.go
@@ -364,7 +364,7 @@ func (t *clientImpl) ListenPacketWithDialer(ctx context.Context, metadata *C.Met
return nil, common.TooManyOpenStreams
}
- pipe1, pipe2 := net.Pipe()
+ pipe1, pipe2 := N.Pipe()
var connId uint32
for {
connId = fastrand.Uint32()
diff --git a/transport/tuic/v5/client.go b/transport/tuic/v5/client.go
index 8a4d6fb1..89454add 100644
--- a/transport/tuic/v5/client.go
+++ b/transport/tuic/v5/client.go
@@ -348,7 +348,7 @@ func (t *clientImpl) ListenPacketWithDialer(ctx context.Context, metadata *C.Met
return nil, common.TooManyOpenStreams
}
- pipe1, pipe2 := net.Pipe()
+ pipe1, pipe2 := N.Pipe()
var connId uint16
for {
connId = uint16(fastrand.Intn(0xFFFF))
diff --git a/tunnel/connection.go b/tunnel/connection.go
index 33cc4e8d..e96545e8 100644
--- a/tunnel/connection.go
+++ b/tunnel/connection.go
@@ -82,6 +82,6 @@ func closeAllLocalCoon(lAddr string) {
})
}
-func handleSocket(ctx C.ConnContext, outbound net.Conn) {
- N.Relay(ctx.Conn(), outbound)
+func handleSocket(inbound, outbound net.Conn) {
+ N.Relay(inbound, outbound)
}
diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go
index 391fe7c1..18c1eb09 100644
--- a/tunnel/tunnel.go
+++ b/tunnel/tunnel.go
@@ -584,7 +584,7 @@ func handleTCPConn(connCtx C.ConnContext) {
peekMutex.Lock()
defer peekMutex.Unlock()
_ = conn.SetReadDeadline(time.Time{}) // reset
- handleSocket(connCtx, remoteConn)
+ handleSocket(conn, remoteConn)
}
func shouldResolveIP(rule C.Rule, metadata *C.Metadata) bool {
From 2e12ceeaed5124129bb1a71993a243524044f93f Mon Sep 17 00:00:00 2001
From: wwqgtxx
Date: Tue, 2 Jan 2024 21:49:27 +0800
Subject: [PATCH 04/34] chore: stop retry when `couldn't find ip`
---
dns/util.go | 6 ++----
tunnel/tunnel.go | 17 +++++++++++++++++
2 files changed, 19 insertions(+), 4 deletions(-)
diff --git a/dns/util.go b/dns/util.go
index f2243917..516c63fb 100644
--- a/dns/util.go
+++ b/dns/util.go
@@ -289,8 +289,6 @@ func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName st
return proxyAdapter.ListenPacketContext(ctx, metadata, opts...)
}
-var errIPNotFound = errors.New("couldn't find ip")
-
func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, cache bool, err error) {
cache = true
fast, ctx := picker.WithTimeout[*D.Msg](ctx, resolver.DefaultDNSTimeout)
@@ -320,12 +318,12 @@ func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.M
case D.TypeAAAA:
if len(ips) == 0 {
noIpMsg = m
- return nil, errIPNotFound
+ return nil, resolver.ErrIPNotFound
}
case D.TypeA:
if len(ips) == 0 {
noIpMsg = m
- return nil, errIPNotFound
+ return nil, resolver.ErrIPNotFound
}
}
}
diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go
index 18c1eb09..cba36d9c 100644
--- a/tunnel/tunnel.go
+++ b/tunnel/tunnel.go
@@ -2,6 +2,7 @@ package tunnel
import (
"context"
+ "errors"
"fmt"
"net"
"net/netip"
@@ -684,6 +685,19 @@ func getRules(metadata *C.Metadata) []C.Rule {
}
}
+func shouldStopRetry(err error) bool {
+ if errors.Is(err, resolver.ErrIPNotFound) {
+ return true
+ }
+ if errors.Is(err, resolver.ErrIPVersion) {
+ return true
+ }
+ if errors.Is(err, resolver.ErrIPv6Disabled) {
+ return true
+ }
+ return false
+}
+
func retry[T any](ctx context.Context, ft func(context.Context) (T, error), fe func(err error)) (t T, err error) {
b := &backoff.Backoff{
Min: 10 * time.Millisecond,
@@ -697,6 +711,9 @@ func retry[T any](ctx context.Context, ft func(context.Context) (T, error), fe f
if fe != nil {
fe(err)
}
+ if shouldStopRetry(err) {
+ return
+ }
select {
case <-time.After(b.Duration()):
continue
From 4af94df142d4ab070e38569a0422ebd7154f8da3 Mon Sep 17 00:00:00 2001
From: "Vincent.Shi"
Date: Fri, 5 Jan 2024 15:07:49 +0800
Subject: [PATCH 05/34] chore: Redundant function calls. (#956)
---
main.go | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/main.go b/main.go
index 425b4661..748fa2e3 100644
--- a/main.go
+++ b/main.go
@@ -72,11 +72,10 @@ func main() {
currentDir, _ := os.Getwd()
configFile = filepath.Join(currentDir, configFile)
}
- C.SetConfig(configFile)
} else {
configFile = filepath.Join(C.Path.HomeDir(), C.Path.Config())
- C.SetConfig(configFile)
}
+ C.SetConfig(configFile)
if geodataMode {
C.GeodataMode = true
From 6bc915526fdf7044ab14ecb89f7a1de7812ea720 Mon Sep 17 00:00:00 2001
From: Larvan2 <78135608+Larvan2@users.noreply.github.com>
Date: Sat, 6 Jan 2024 17:54:09 +0800
Subject: [PATCH 06/34] fix: resolve IPv6 rule-set issue #959.
---
component/trie/ipcidr_trie.go | 4 ++++
component/trie/trie_test.go | 8 ++++++++
2 files changed, 12 insertions(+)
diff --git a/component/trie/ipcidr_trie.go b/component/trie/ipcidr_trie.go
index a2ccfa16..ee8659be 100644
--- a/component/trie/ipcidr_trie.go
+++ b/component/trie/ipcidr_trie.go
@@ -185,6 +185,10 @@ func addIpv6Cidr(trie *IpCidrTrie, ip net.IP, groupSize int) {
}
for i := 2; i < groupSize; i += 2 {
+ if ip[i] == 0 && ip[i+1] == 0 {
+ node.Mark = true
+ }
+
if node.Mark {
return
}
diff --git a/component/trie/trie_test.go b/component/trie/trie_test.go
index e1b20103..a589b7ca 100644
--- a/component/trie/trie_test.go
+++ b/component/trie/trie_test.go
@@ -74,6 +74,14 @@ func TestIpv6AddFail(t *testing.T) {
assert.IsType(t, new(net.ParseError), err)
}
+func TestIpv6SearchSub(t *testing.T) {
+ trie := NewIpCidrTrie()
+ assert.NoError(t, trie.AddIpCidrForString("240e::/18"))
+
+ assert.Equal(t, true, trie.IsContainForString("240e:964:ea02:100:1800::71"))
+
+}
+
func TestIpv6Search(t *testing.T) {
trie := NewIpCidrTrie()
From 7e3e38d0548a738d36ff8d4e92567f2cab16863e Mon Sep 17 00:00:00 2001
From: Cesaryuan <35998162+cesaryuan@users.noreply.github.com>
Date: Sat, 6 Jan 2024 22:25:51 +0800
Subject: [PATCH 07/34] fix: SUB-RULE with PROCESS-NAME rule payload not
working (#953)
---
rules/logic/logic.go | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/rules/logic/logic.go b/rules/logic/logic.go
index fde96e19..af8c31a4 100644
--- a/rules/logic/logic.go
+++ b/rules/logic/logic.go
@@ -217,6 +217,13 @@ func (logic *Logic) parsePayload(payload string, parseRule ParseRuleFunc) error
return err
}
+ if rule.ShouldResolveIP() {
+ logic.needIP = true
+ }
+ if rule.ShouldFindProcess() {
+ logic.needProcess = true
+ }
+
rules = append(rules, rule)
}
From ffcd672ebf1412de62915b754150f562873579f6 Mon Sep 17 00:00:00 2001
From: xishang0128
Date: Sun, 7 Jan 2024 23:32:22 +0800
Subject: [PATCH 08/34] chore: return more information for the api
---
adapter/outboundgroup/fallback.go | 6 ++++++
adapter/outboundgroup/loadbalance.go | 6 ++++++
adapter/outboundgroup/parser.go | 2 ++
adapter/outboundgroup/relay.go | 11 +++++++++--
adapter/outboundgroup/selector.go | 12 +++++++++---
adapter/outboundgroup/urltest.go | 6 ++++++
6 files changed, 38 insertions(+), 5 deletions(-)
diff --git a/adapter/outboundgroup/fallback.go b/adapter/outboundgroup/fallback.go
index 488c2d34..9db97cf3 100644
--- a/adapter/outboundgroup/fallback.go
+++ b/adapter/outboundgroup/fallback.go
@@ -21,6 +21,8 @@ type Fallback struct {
testUrl string
selected string
expectedStatus string
+ Hidden bool
+ Icon string
}
func (f *Fallback) Now() string {
@@ -90,6 +92,8 @@ func (f *Fallback) MarshalJSON() ([]byte, error) {
"testUrl": f.testUrl,
"expectedStatus": f.expectedStatus,
"fixed": f.selected,
+ "hidden": f.Hidden,
+ "icon": f.Icon,
})
}
@@ -165,5 +169,7 @@ func NewFallback(option *GroupCommonOption, providers []provider.ProxyProvider)
disableUDP: option.DisableUDP,
testUrl: option.URL,
expectedStatus: option.ExpectedStatus,
+ Hidden: option.Hidden,
+ Icon: option.Icon,
}
}
diff --git a/adapter/outboundgroup/loadbalance.go b/adapter/outboundgroup/loadbalance.go
index 79b18948..976a2e89 100644
--- a/adapter/outboundgroup/loadbalance.go
+++ b/adapter/outboundgroup/loadbalance.go
@@ -29,6 +29,8 @@ type LoadBalance struct {
strategyFn strategyFn
testUrl string
expectedStatus string
+ Hidden bool
+ Icon string
}
var errStrategy = errors.New("unsupported strategy")
@@ -236,6 +238,8 @@ func (lb *LoadBalance) MarshalJSON() ([]byte, error) {
"all": all,
"testUrl": lb.testUrl,
"expectedStatus": lb.expectedStatus,
+ "hidden": lb.Hidden,
+ "icon": lb.Icon,
})
}
@@ -268,5 +272,7 @@ func NewLoadBalance(option *GroupCommonOption, providers []provider.ProxyProvide
disableUDP: option.DisableUDP,
testUrl: option.URL,
expectedStatus: option.ExpectedStatus,
+ Hidden: option.Hidden,
+ Icon: option.Icon,
}, nil
}
diff --git a/adapter/outboundgroup/parser.go b/adapter/outboundgroup/parser.go
index 3f7f9770..de20c6ea 100644
--- a/adapter/outboundgroup/parser.go
+++ b/adapter/outboundgroup/parser.go
@@ -37,6 +37,8 @@ type GroupCommonOption struct {
IncludeAll bool `group:"include-all,omitempty"`
IncludeAllProxies bool `group:"include-all-proxies,omitempty"`
IncludeAllProviders bool `group:"include-all-providers,omitempty"`
+ Hidden bool `group:"hidden,omitempty"`
+ Icon string `group:"icon,omitempty"`
}
func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, providersMap map[string]types.ProxyProvider, AllProxies []string, AllProviders []string) (C.ProxyAdapter, error) {
diff --git a/adapter/outboundgroup/relay.go b/adapter/outboundgroup/relay.go
index 2b1be8a5..6a8e8bb1 100644
--- a/adapter/outboundgroup/relay.go
+++ b/adapter/outboundgroup/relay.go
@@ -3,6 +3,7 @@ package outboundgroup
import (
"context"
"encoding/json"
+
"github.com/metacubex/mihomo/adapter/outbound"
"github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/proxydialer"
@@ -12,6 +13,8 @@ import (
type Relay struct {
*GroupBase
+ Hidden bool
+ Icon string
}
// DialContext implements C.ProxyAdapter
@@ -106,8 +109,10 @@ func (r *Relay) MarshalJSON() ([]byte, error) {
all = append(all, proxy.Name())
}
return json.Marshal(map[string]any{
- "type": r.Type().String(),
- "all": all,
+ "type": r.Type().String(),
+ "all": all,
+ "hidden": r.Hidden,
+ "icon": r.Icon,
})
}
@@ -157,5 +162,7 @@ func NewRelay(option *GroupCommonOption, providers []provider.ProxyProvider) *Re
"",
providers,
}),
+ Hidden: option.Hidden,
+ Icon: option.Icon,
}
}
diff --git a/adapter/outboundgroup/selector.go b/adapter/outboundgroup/selector.go
index 4d06c544..3ac740f4 100644
--- a/adapter/outboundgroup/selector.go
+++ b/adapter/outboundgroup/selector.go
@@ -15,6 +15,8 @@ type Selector struct {
*GroupBase
disableUDP bool
selected string
+ Hidden bool
+ Icon string
}
// DialContext implements C.ProxyAdapter
@@ -57,9 +59,11 @@ func (s *Selector) MarshalJSON() ([]byte, error) {
}
return json.Marshal(map[string]any{
- "type": s.Type().String(),
- "now": s.Now(),
- "all": all,
+ "type": s.Type().String(),
+ "now": s.Now(),
+ "all": all,
+ "hidden": s.Hidden,
+ "icon": s.Icon,
})
}
@@ -114,5 +118,7 @@ func NewSelector(option *GroupCommonOption, providers []provider.ProxyProvider)
}),
selected: "COMPATIBLE",
disableUDP: option.DisableUDP,
+ Hidden: option.Hidden,
+ Icon: option.Icon,
}
}
diff --git a/adapter/outboundgroup/urltest.go b/adapter/outboundgroup/urltest.go
index 2f9456fa..8439772c 100644
--- a/adapter/outboundgroup/urltest.go
+++ b/adapter/outboundgroup/urltest.go
@@ -33,6 +33,8 @@ type URLTest struct {
expectedStatus string
tolerance uint16
disableUDP bool
+ Hidden bool
+ Icon string
fastNode C.Proxy
fastSingle *singledo.Single[C.Proxy]
}
@@ -174,6 +176,8 @@ func (u *URLTest) MarshalJSON() ([]byte, error) {
"testUrl": u.testUrl,
"expectedStatus": u.expectedStatus,
"fixed": u.selected,
+ "hidden": u.Hidden,
+ "icon": u.Icon,
})
}
@@ -237,6 +241,8 @@ func NewURLTest(option *GroupCommonOption, providers []provider.ProxyProvider, o
disableUDP: option.DisableUDP,
testUrl: option.URL,
expectedStatus: option.ExpectedStatus,
+ Hidden: option.Hidden,
+ Icon: option.Icon,
}
for _, option := range options {
From 425bc692add830105198e03544f25344611af29e Mon Sep 17 00:00:00 2001
From: Larvan2 <78135608+Larvan2@users.noreply.github.com>
Date: Tue, 9 Jan 2024 16:37:34 +0800
Subject: [PATCH 09/34] chore: replace IpCidrTrie with binary search
---
component/cidr/ipcidr_set.go | 89 +++++++++++++++++++++++++
component/cidr/ipcidr_set_test.go | 105 ++++++++++++++++++++++++++++++
rules/provider/ipcidr_strategy.go | 18 +++--
3 files changed, 206 insertions(+), 6 deletions(-)
create mode 100644 component/cidr/ipcidr_set.go
create mode 100644 component/cidr/ipcidr_set_test.go
diff --git a/component/cidr/ipcidr_set.go b/component/cidr/ipcidr_set.go
new file mode 100644
index 00000000..b8dec0ee
--- /dev/null
+++ b/component/cidr/ipcidr_set.go
@@ -0,0 +1,89 @@
+package cidr
+
+import (
+ "math/big"
+ "net"
+ "sort"
+)
+
+type Range struct {
+ Start *big.Int
+ End *big.Int
+}
+
+type IpCidrSet struct {
+ Ranges []Range
+}
+
+func NewIpCidrSet() *IpCidrSet {
+ return &IpCidrSet{}
+}
+
+func ipToBigInt(ip net.IP) *big.Int {
+ ipBigInt := big.NewInt(0)
+ ipBigInt.SetBytes(ip.To16())
+ return ipBigInt
+}
+
+func cidrToRange(cidr string) (Range, error) {
+ _, ipNet, err := net.ParseCIDR(cidr)
+ if err != nil {
+ return Range{}, err
+ }
+ firstIP, lastIP := networkRange(ipNet)
+ return Range{Start: ipToBigInt(firstIP), End: ipToBigInt(lastIP)}, nil
+}
+
+func networkRange(network *net.IPNet) (net.IP, net.IP) {
+ firstIP := network.IP
+ lastIP := make(net.IP, len(firstIP))
+ copy(lastIP, firstIP)
+ for i := range firstIP {
+ lastIP[i] |= ^network.Mask[i]
+ }
+ return firstIP, lastIP
+}
+
+func (set *IpCidrSet) AddIpCidrForString(ipCidr string) error {
+ ipRange, err := cidrToRange(ipCidr)
+ if err != nil {
+ return err
+ }
+ set.Ranges = append(set.Ranges, ipRange)
+ sort.Slice(set.Ranges, func(i, j int) bool {
+ return set.Ranges[i].Start.Cmp(set.Ranges[j].Start) < 0
+ })
+ return nil
+}
+
+func (set *IpCidrSet) AddIpCidr(ipCidr *net.IPNet) error {
+ return set.AddIpCidrForString(ipCidr.String())
+}
+
+func (set *IpCidrSet) IsContainForString(ipString string) bool {
+ ip := ipToBigInt(net.ParseIP(ipString))
+ idx := sort.Search(len(set.Ranges), func(i int) bool {
+ return set.Ranges[i].End.Cmp(ip) >= 0
+ })
+ if idx < len(set.Ranges) && set.Ranges[idx].Start.Cmp(ip) <= 0 && set.Ranges[idx].End.Cmp(ip) >= 0 {
+ return true
+ }
+ return false
+}
+
+func (set *IpCidrSet) IsContain(ip net.IP) bool {
+ if ip == nil {
+ return false
+ }
+ return set.IsContainForString(ip.String())
+}
+
+func (set *IpCidrSet) Merge() {
+ for i := 0; i < len(set.Ranges)-1; i++ {
+ if set.Ranges[i].End.Cmp(set.Ranges[i+1].Start) >= 0 {
+ set.Ranges[i].End = set.Ranges[i+1].End
+ set.Ranges = append(set.Ranges[:i+1], set.Ranges[i+2:]...)
+ i--
+ }
+ }
+}
diff --git a/component/cidr/ipcidr_set_test.go b/component/cidr/ipcidr_set_test.go
new file mode 100644
index 00000000..a8cfef7f
--- /dev/null
+++ b/component/cidr/ipcidr_set_test.go
@@ -0,0 +1,105 @@
+package cidr
+
+import (
+ "testing"
+)
+
+func TestIpv4(t *testing.T) {
+ tests := []struct {
+ name string
+ ipCidr string
+ ip string
+ expected bool
+ }{
+ {
+ name: "Test Case 1",
+ ipCidr: "149.154.160.0/20",
+ ip: "149.154.160.0",
+ expected: true,
+ },
+ {
+ name: "Test Case 2",
+ ipCidr: "192.168.0.0/16",
+ ip: "10.0.0.1",
+ expected: false,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ set := &IpCidrSet{}
+ set.AddIpCidrForString(test.ipCidr)
+
+ result := set.IsContainForString(test.ip)
+ if result != test.expected {
+ t.Errorf("Expected result: %v, got: %v", test.expected, result)
+ }
+ })
+ }
+}
+
+func TestIpv6(t *testing.T) {
+ tests := []struct {
+ name string
+ ipCidr string
+ ip string
+ expected bool
+ }{
+ {
+ name: "Test Case 1",
+ ipCidr: "2409:8000::/20",
+ ip: "2409:8087:1e03:21::27",
+ expected: true,
+ },
+ {
+ name: "Test Case 2",
+ ipCidr: "240e::/16",
+ ip: "240e:964:ea02:100:1800::71",
+ expected: true,
+ },
+ }
+ // Add more test cases as needed
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ set := &IpCidrSet{}
+ set.AddIpCidrForString(test.ipCidr)
+
+ result := set.IsContainForString(test.ip)
+ if result != test.expected {
+ t.Errorf("Expected result: %v, got: %v", test.expected, result)
+ }
+ })
+ }
+}
+
+func TestMerge(t *testing.T) {
+ tests := []struct {
+ name string
+ ipCidr1 string
+ ipCidr2 string
+ ipCidr3 string
+ expectedLen int
+ }{
+ {
+ name: "Test Case 1",
+ ipCidr1: "2409:8000::/20",
+ ipCidr2: "2409:8000::/21",
+ ipCidr3: "2409:8000::/48",
+ expectedLen: 1,
+ },
+ }
+
+ for _, test := range tests {
+ t.Run(test.name, func(t *testing.T) {
+ set := &IpCidrSet{}
+ set.AddIpCidrForString(test.ipCidr1)
+ set.AddIpCidrForString(test.ipCidr2)
+ set.Merge()
+
+ if len(set.Ranges) != test.expectedLen {
+ t.Errorf("Expected len: %v, got: %v", test.expectedLen, len(set.Ranges))
+ }
+ })
+ }
+}
diff --git a/rules/provider/ipcidr_strategy.go b/rules/provider/ipcidr_strategy.go
index 321e901a..dad48305 100644
--- a/rules/provider/ipcidr_strategy.go
+++ b/rules/provider/ipcidr_strategy.go
@@ -1,7 +1,7 @@
package provider
import (
- "github.com/metacubex/mihomo/component/trie"
+ "github.com/metacubex/mihomo/component/cidr"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/log"
)
@@ -9,7 +9,8 @@ import (
type ipcidrStrategy struct {
count int
shouldResolveIP bool
- trie *trie.IpCidrTrie
+ cidrSet *cidr.IpCidrSet
+ //trie *trie.IpCidrTrie
}
func (i *ipcidrStrategy) ShouldFindProcess() bool {
@@ -17,7 +18,8 @@ func (i *ipcidrStrategy) ShouldFindProcess() bool {
}
func (i *ipcidrStrategy) Match(metadata *C.Metadata) bool {
- return i.trie != nil && i.trie.IsContain(metadata.DstIP.AsSlice())
+ // return i.trie != nil && i.trie.IsContain(metadata.DstIP.AsSlice())
+ return i.cidrSet != nil && i.cidrSet.IsContain(metadata.DstIP.AsSlice())
}
func (i *ipcidrStrategy) Count() int {
@@ -29,13 +31,15 @@ func (i *ipcidrStrategy) ShouldResolveIP() bool {
}
func (i *ipcidrStrategy) Reset() {
- i.trie = trie.NewIpCidrTrie()
+ // i.trie = trie.NewIpCidrTrie()
+ i.cidrSet = cidr.NewIpCidrSet()
i.count = 0
i.shouldResolveIP = false
}
func (i *ipcidrStrategy) Insert(rule string) {
- err := i.trie.AddIpCidrForString(rule)
+ //err := i.trie.AddIpCidrForString(rule)
+ err := i.cidrSet.AddIpCidrForString(rule)
if err != nil {
log.Warnln("invalid Ipcidr:[%s]", rule)
} else {
@@ -44,7 +48,9 @@ func (i *ipcidrStrategy) Insert(rule string) {
}
}
-func (i *ipcidrStrategy) FinishInsert() {}
+func (i *ipcidrStrategy) FinishInsert() {
+ i.cidrSet.Merge()
+}
func NewIPCidrStrategy() *ipcidrStrategy {
return &ipcidrStrategy{}
From 7c54775ddcec7c9dd2e7f2688c69bcbcd51f68cb Mon Sep 17 00:00:00 2001
From: wwqgtxx
Date: Thu, 11 Jan 2024 09:33:59 +0800
Subject: [PATCH 10/34] chore: ipcidr direct using go4.org/netipx
---
component/cidr/ipcidr_set.go | 81 ++++++++-----------------------
component/cidr/ipcidr_set_test.go | 8 ++-
go.mod | 2 +-
rules/provider/ipcidr_strategy.go | 2 +-
4 files changed, 28 insertions(+), 65 deletions(-)
diff --git a/component/cidr/ipcidr_set.go b/component/cidr/ipcidr_set.go
index b8dec0ee..dac2d38a 100644
--- a/component/cidr/ipcidr_set.go
+++ b/component/cidr/ipcidr_set.go
@@ -1,89 +1,48 @@
package cidr
import (
- "math/big"
- "net"
- "sort"
+ "go4.org/netipx"
+ "net/netip"
)
-type Range struct {
- Start *big.Int
- End *big.Int
-}
-
type IpCidrSet struct {
- Ranges []Range
+ Ranges *netipx.IPSet
}
func NewIpCidrSet() *IpCidrSet {
return &IpCidrSet{}
}
-func ipToBigInt(ip net.IP) *big.Int {
- ipBigInt := big.NewInt(0)
- ipBigInt.SetBytes(ip.To16())
- return ipBigInt
-}
-
-func cidrToRange(cidr string) (Range, error) {
- _, ipNet, err := net.ParseCIDR(cidr)
- if err != nil {
- return Range{}, err
- }
- firstIP, lastIP := networkRange(ipNet)
- return Range{Start: ipToBigInt(firstIP), End: ipToBigInt(lastIP)}, nil
-}
-
-func networkRange(network *net.IPNet) (net.IP, net.IP) {
- firstIP := network.IP
- lastIP := make(net.IP, len(firstIP))
- copy(lastIP, firstIP)
- for i := range firstIP {
- lastIP[i] |= ^network.Mask[i]
- }
- return firstIP, lastIP
-}
-
func (set *IpCidrSet) AddIpCidrForString(ipCidr string) error {
- ipRange, err := cidrToRange(ipCidr)
+ prefix, err := netip.ParsePrefix(ipCidr)
if err != nil {
return err
}
- set.Ranges = append(set.Ranges, ipRange)
- sort.Slice(set.Ranges, func(i, j int) bool {
- return set.Ranges[i].Start.Cmp(set.Ranges[j].Start) < 0
- })
+ err = set.AddIpCidr(prefix)
return nil
}
-func (set *IpCidrSet) AddIpCidr(ipCidr *net.IPNet) error {
- return set.AddIpCidrForString(ipCidr.String())
+func (set *IpCidrSet) AddIpCidr(ipCidr netip.Prefix) (err error) {
+ var b netipx.IPSetBuilder
+ b.AddSet(set.Ranges)
+ b.AddPrefix(ipCidr)
+ set.Ranges, err = b.IPSet()
+ return
}
func (set *IpCidrSet) IsContainForString(ipString string) bool {
- ip := ipToBigInt(net.ParseIP(ipString))
- idx := sort.Search(len(set.Ranges), func(i int) bool {
- return set.Ranges[i].End.Cmp(ip) >= 0
- })
- if idx < len(set.Ranges) && set.Ranges[idx].Start.Cmp(ip) <= 0 && set.Ranges[idx].End.Cmp(ip) >= 0 {
- return true
- }
- return false
-}
-
-func (set *IpCidrSet) IsContain(ip net.IP) bool {
- if ip == nil {
+ ip, err := netip.ParseAddr(ipString)
+ if err != nil {
return false
}
- return set.IsContainForString(ip.String())
+ return set.IsContain(ip)
}
-func (set *IpCidrSet) Merge() {
- for i := 0; i < len(set.Ranges)-1; i++ {
- if set.Ranges[i].End.Cmp(set.Ranges[i+1].Start) >= 0 {
- set.Ranges[i].End = set.Ranges[i+1].End
- set.Ranges = append(set.Ranges[:i+1], set.Ranges[i+2:]...)
- i--
- }
+func (set *IpCidrSet) IsContain(ip netip.Addr) bool {
+ if set.Ranges == nil {
+ return false
}
+ return set.Ranges.Contains(ip.WithZone(""))
}
+
+func (set *IpCidrSet) Merge() {}
diff --git a/component/cidr/ipcidr_set_test.go b/component/cidr/ipcidr_set_test.go
index a8cfef7f..a6eaec84 100644
--- a/component/cidr/ipcidr_set_test.go
+++ b/component/cidr/ipcidr_set_test.go
@@ -1,7 +1,9 @@
package cidr
import (
+ "go4.org/netipx"
"testing"
+ "unsafe"
)
func TestIpv4(t *testing.T) {
@@ -97,8 +99,10 @@ func TestMerge(t *testing.T) {
set.AddIpCidrForString(test.ipCidr2)
set.Merge()
- if len(set.Ranges) != test.expectedLen {
- t.Errorf("Expected len: %v, got: %v", test.expectedLen, len(set.Ranges))
+ rangesLen := len(*(*[]netipx.IPRange)(unsafe.Pointer(set.Ranges)))
+
+ if rangesLen != test.expectedLen {
+ t.Errorf("Expected len: %v, got: %v", test.expectedLen, rangesLen)
}
})
}
diff --git a/go.mod b/go.mod
index 866ac035..07458af8 100644
--- a/go.mod
+++ b/go.mod
@@ -47,6 +47,7 @@ require (
github.com/wk8/go-ordered-map/v2 v2.1.8
github.com/zhangyunhao116/fastrand v0.3.0
go.uber.org/automaxprocs v1.5.3
+ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
golang.org/x/crypto v0.16.0
golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb
golang.org/x/net v0.19.0
@@ -105,7 +106,6 @@ require (
github.com/yusufpapurcu/wmi v1.2.3 // indirect
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect
go.uber.org/mock v0.3.0 // indirect
- go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
diff --git a/rules/provider/ipcidr_strategy.go b/rules/provider/ipcidr_strategy.go
index dad48305..c93facd9 100644
--- a/rules/provider/ipcidr_strategy.go
+++ b/rules/provider/ipcidr_strategy.go
@@ -19,7 +19,7 @@ func (i *ipcidrStrategy) ShouldFindProcess() bool {
func (i *ipcidrStrategy) Match(metadata *C.Metadata) bool {
// return i.trie != nil && i.trie.IsContain(metadata.DstIP.AsSlice())
- return i.cidrSet != nil && i.cidrSet.IsContain(metadata.DstIP.AsSlice())
+ return i.cidrSet != nil && i.cidrSet.IsContain(metadata.DstIP)
}
func (i *ipcidrStrategy) Count() int {
From 5ec686df08fa885e49d2df088586a9f2bc5ead0f Mon Sep 17 00:00:00 2001
From: wwqgtxx
Date: Thu, 11 Jan 2024 10:00:02 +0800
Subject: [PATCH 11/34] chore: update dependencies
---
go.mod | 28 ++++++++++++++--------------
go.sum | 54 ++++++++++++++++++++++++++----------------------------
2 files changed, 40 insertions(+), 42 deletions(-)
diff --git a/go.mod b/go.mod
index 07458af8..f8252128 100644
--- a/go.mod
+++ b/go.mod
@@ -9,10 +9,10 @@ require (
github.com/cilium/ebpf v0.12.3
github.com/coreos/go-iptables v0.7.0
github.com/dlclark/regexp2 v1.10.0
- github.com/go-chi/chi/v5 v5.0.10
+ github.com/go-chi/chi/v5 v5.0.11
github.com/go-chi/cors v1.2.1
github.com/go-chi/render v1.0.3
- github.com/gobwas/ws v1.3.1
+ github.com/gobwas/ws v1.3.2
github.com/gofrs/uuid/v5 v5.0.0
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2
github.com/jpillora/backoff v1.0.0
@@ -23,8 +23,8 @@ require (
github.com/metacubex/quic-go v0.40.1-0.20231130135418-0c1b47cf9394
github.com/metacubex/sing-quic v0.0.0-20231220152840-85620b446796
github.com/metacubex/sing-shadowsocks v0.2.6
- github.com/metacubex/sing-shadowsocks2 v0.1.6-beta.1
- github.com/metacubex/sing-tun v0.2.0-beta.4
+ github.com/metacubex/sing-shadowsocks2 v0.2.0
+ github.com/metacubex/sing-tun v0.2.0
github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f
github.com/metacubex/sing-wireguard v0.0.0-20231209125515-0594297f7232
github.com/miekg/dns v1.1.57
@@ -34,26 +34,26 @@ require (
github.com/puzpuzpuz/xsync/v3 v3.0.2
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
- github.com/sagernet/sing v0.3.0-rc.3
- github.com/sagernet/sing-mux v0.1.6-beta.1
+ github.com/sagernet/sing v0.3.0
+ github.com/sagernet/sing-mux v0.2.0
github.com/sagernet/sing-shadowtls v0.1.4
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6
github.com/sagernet/utls v1.5.4
github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e
github.com/samber/lo v1.39.0
- github.com/shirou/gopsutil/v3 v3.23.11
+ github.com/shirou/gopsutil/v3 v3.23.12
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.8.4
github.com/wk8/go-ordered-map/v2 v2.1.8
github.com/zhangyunhao116/fastrand v0.3.0
go.uber.org/automaxprocs v1.5.3
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba
- golang.org/x/crypto v0.16.0
- golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb
- golang.org/x/net v0.19.0
- golang.org/x/sync v0.5.0
- golang.org/x/sys v0.15.0
- google.golang.org/protobuf v1.31.0
+ golang.org/x/crypto v0.18.0
+ golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e
+ golang.org/x/net v0.20.0
+ golang.org/x/sync v0.6.0
+ golang.org/x/sys v0.16.0
+ google.golang.org/protobuf v1.32.0
gopkg.in/yaml.v3 v3.0.1
lukechampine.com/blake3 v1.2.1
)
@@ -112,4 +112,4 @@ require (
golang.org/x/tools v0.16.0 // indirect
)
-replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20231221131356-d73c21c7ea3f
+replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240111014253-f1818b6a82b2
diff --git a/go.sum b/go.sum
index 3f80c92d..c3209df8 100644
--- a/go.sum
+++ b/go.sum
@@ -44,8 +44,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk=
github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
-github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk=
-github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
+github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA=
+github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
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.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
@@ -60,16 +60,14 @@ github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
-github.com/gobwas/ws v1.3.1 h1:Qi34dfLMWJbiKaNbDVzM9x27nZBjmkaW6i4+Ku+pGVU=
-github.com/gobwas/ws v1.3.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
+github.com/gobwas/ws v1.3.2 h1:zlnbNHxumkRvfPWgfXu8RBwyNR1x8wh9cf5PTOCqs9Q=
+github.com/gobwas/ws v1.3.2/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
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/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.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
-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/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
@@ -110,16 +108,16 @@ github.com/metacubex/gvisor v0.0.0-20231209122014-3e43224c7bbc h1:+yTZ6q2EeQCAJN
github.com/metacubex/gvisor v0.0.0-20231209122014-3e43224c7bbc/go.mod h1:rhBU9tD5ktoGPBtXUquhWuGJ4u+8ZZzBMi2cAdv9q8Y=
github.com/metacubex/quic-go v0.40.1-0.20231130135418-0c1b47cf9394 h1:dIT+KB2hknBCrwVAXPeY9tpzzkOZP5m40yqUteRT6/Y=
github.com/metacubex/quic-go v0.40.1-0.20231130135418-0c1b47cf9394/go.mod h1:F/t8VnA47xoia8ABlNA4InkZjssvFJ5p6E6jKdbkgAs=
-github.com/metacubex/sing v0.0.0-20231221131356-d73c21c7ea3f h1:T2PuaAiXMSC3mjRRUmIomuiu3jhi7EWSbzXtVIrVUC4=
-github.com/metacubex/sing v0.0.0-20231221131356-d73c21c7ea3f/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g=
+github.com/metacubex/sing v0.0.0-20240111014253-f1818b6a82b2 h1:upEO8dt9WDBavhgcgkXB3hRcwVNbkTbnd+xyzy6ZQZo=
+github.com/metacubex/sing v0.0.0-20240111014253-f1818b6a82b2/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g=
github.com/metacubex/sing-quic v0.0.0-20231220152840-85620b446796 h1:xiCPttMGAaIh4Ad6t85VxUoUv+Sg88eXzzUvYN8gT5w=
github.com/metacubex/sing-quic v0.0.0-20231220152840-85620b446796/go.mod h1:E1e1Uu6YaJddD+c0DtJlSOkfMI0NLdOVhM60KAlcssY=
github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ=
github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg=
-github.com/metacubex/sing-shadowsocks2 v0.1.6-beta.1 h1:ftbpVCK1+n3jxIP7+NMkRYOFEQtGPodV42MizsPey0w=
-github.com/metacubex/sing-shadowsocks2 v0.1.6-beta.1/go.mod h1:kUVj2X+2wUh6Z5pAk9WrjDRehPyXolC6nJyFl7ln4V4=
-github.com/metacubex/sing-tun v0.2.0-beta.4 h1:42F+uF9zKsaWsiUXNKzZD8aRkyPN9m5SdpF2yZEZar8=
-github.com/metacubex/sing-tun v0.2.0-beta.4/go.mod h1:O8wFThUDfiwb6y56I714dQuyaqT8DW9VCD/wvGesyLM=
+github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A=
+github.com/metacubex/sing-shadowsocks2 v0.2.0/go.mod h1:LCKF6j1P94zN8ZS+LXRK1gmYTVGB3squivBSXAFnOg8=
+github.com/metacubex/sing-tun v0.2.0 h1:CrWgFI58umlbrYa8RSygAMQ37dESpIX0z/Ruj6+2dZY=
+github.com/metacubex/sing-tun v0.2.0/go.mod h1:P+TjrGTG5AdQRaskP6NiI9gZmgnwR3o5ze9CkIQE+/s=
github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f h1:QjXrHKbTMBip/C+R79bvbfr42xH1gZl3uFb0RELdZiQ=
github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY=
github.com/metacubex/sing-wireguard v0.0.0-20231209125515-0594297f7232 h1:loWjR+k9dxqBSgruGyT5hE8UCRMmCEjxqZbryfY9no4=
@@ -159,8 +157,8 @@ github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkk
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM=
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-mux v0.1.6-beta.1 h1:ADs1TgiMfA628Y2qfv21tEvePDZjBRRYddwtNFZiwe8=
-github.com/sagernet/sing-mux v0.1.6-beta.1/go.mod h1:WWtRmrwCDgb+g+7Da6o62I9WiMNB0a3w6BJhEpNQlNA=
+github.com/sagernet/sing-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo=
+github.com/sagernet/sing-mux v0.2.0/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ=
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
@@ -175,8 +173,8 @@ github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA=
github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg=
github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s=
-github.com/shirou/gopsutil/v3 v3.23.11 h1:i3jP9NjCPUz7FiZKxlMnODZkdSIp2gnzfrvsu9CuWEQ=
-github.com/shirou/gopsutil/v3 v3.23.11/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
+github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4=
+github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
@@ -226,21 +224,21 @@ go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBs
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
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.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
-golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
-golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb h1:c0vyKkb6yr3KR7jEfJaOSv4lG7xPkbN6r52aJz1d8a8=
-golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
+golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
+golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
+golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e h1:723BNChdd0c2Wk6WOE320qGBiPtYx0F0Bbm1kriShfE=
+golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
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.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
-golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
+golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
+golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
-golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
+golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -256,8 +254,9 @@ 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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
+golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
@@ -268,9 +267,8 @@ golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM=
golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
-google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
+google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
From d2d8c0115cb464b93ffee04f4768ebc35d49d4ec Mon Sep 17 00:00:00 2001
From: Ahmad Nazari
Date: Thu, 11 Jan 2024 06:10:04 +0330
Subject: [PATCH 12/34] fix: flush dns cache in android and cmfa build. (#971)
---
dns/patch_android.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dns/patch_android.go b/dns/patch_android.go
index 2e9a6b9f..e62aabcd 100644
--- a/dns/patch_android.go
+++ b/dns/patch_android.go
@@ -49,7 +49,7 @@ func ServeDNSWithDefaultServer(msg *D.Msg) (*D.Msg, error) {
func FlushCacheWithDefaultResolver() {
if r := resolver.DefaultResolver; r != nil {
- r.(*Resolver).lruCache = lru.New[string, *D.Msg](lru.WithSize[string, *D.Msg](4096), lru.WithStale[string, *D.Msg](true))
+ r.(*Resolver).cache = lru.New(lru.WithSize[string, *D.Msg](4096), lru.WithStale[string, *D.Msg](true))
}
}
From e860497c0c92bfc765fd0caa4c4b61b85c23ee81 Mon Sep 17 00:00:00 2001
From: wwqgtxx
Date: Sat, 13 Jan 2024 11:44:02 +0800
Subject: [PATCH 13/34] chore: cleanup IPSet code
---
component/geodata/router/condition.go | 206 +++-----------------------
dns/filters.go | 2 +-
rules/common/geoip.go | 2 +-
3 files changed, 20 insertions(+), 190 deletions(-)
diff --git a/component/geodata/router/condition.go b/component/geodata/router/condition.go
index 73bc88d4..c2ac8071 100644
--- a/component/geodata/router/condition.go
+++ b/component/geodata/router/condition.go
@@ -1,12 +1,11 @@
package router
import (
- "encoding/binary"
"fmt"
- "net"
- "sort"
+ "net/netip"
"strings"
+ "github.com/metacubex/mihomo/component/cidr"
"github.com/metacubex/mihomo/component/geodata/strmatcher"
"github.com/metacubex/mihomo/component/trie"
)
@@ -121,115 +120,24 @@ func (m *v2rayDomainMatcher) ApplyDomain(domain string) bool {
return isMatched
}
-// CIDRList is an alias of []*CIDR to provide sort.Interface.
-type CIDRList []*CIDR
-
-// Len implements sort.Interface.
-func (l *CIDRList) Len() int {
- return len(*l)
-}
-
-// Less implements sort.Interface.
-func (l *CIDRList) Less(i int, j int) bool {
- ci := (*l)[i]
- cj := (*l)[j]
-
- if len(ci.Ip) < len(cj.Ip) {
- return true
- }
-
- if len(ci.Ip) > len(cj.Ip) {
- return false
- }
-
- for k := 0; k < len(ci.Ip); k++ {
- if ci.Ip[k] < cj.Ip[k] {
- return true
- }
-
- if ci.Ip[k] > cj.Ip[k] {
- return false
- }
- }
-
- return ci.Prefix < cj.Prefix
-}
-
-// Swap implements sort.Interface.
-func (l *CIDRList) Swap(i int, j int) {
- (*l)[i], (*l)[j] = (*l)[j], (*l)[i]
-}
-
-type ipv6 struct {
- a uint64
- b uint64
-}
-
type GeoIPMatcher struct {
countryCode string
reverseMatch bool
- ip4 []uint32
- prefix4 []uint8
- ip6 []ipv6
- prefix6 []uint8
-}
-
-func normalize4(ip uint32, prefix uint8) uint32 {
- return (ip >> (32 - prefix)) << (32 - prefix)
-}
-
-func normalize6(ip ipv6, prefix uint8) ipv6 {
- if prefix <= 64 {
- ip.a = (ip.a >> (64 - prefix)) << (64 - prefix)
- ip.b = 0
- } else {
- ip.b = (ip.b >> (128 - prefix)) << (128 - prefix)
- }
- return ip
+ cidrSet *cidr.IpCidrSet
}
func (m *GeoIPMatcher) Init(cidrs []*CIDR) error {
- ip4Count := 0
- ip6Count := 0
-
for _, cidr := range cidrs {
- ip := cidr.Ip
- switch len(ip) {
- case 4:
- ip4Count++
- case 16:
- ip6Count++
- default:
- return fmt.Errorf("unexpect ip length: %d", len(ip))
- }
- }
-
- cidrList := CIDRList(cidrs)
- sort.Sort(&cidrList)
-
- m.ip4 = make([]uint32, 0, ip4Count)
- m.prefix4 = make([]uint8, 0, ip4Count)
- m.ip6 = make([]ipv6, 0, ip6Count)
- m.prefix6 = make([]uint8, 0, ip6Count)
-
- for _, cidr := range cidrs {
- ip := cidr.Ip
- prefix := uint8(cidr.Prefix)
- switch len(ip) {
- case 4:
- m.ip4 = append(m.ip4, normalize4(binary.BigEndian.Uint32(ip), prefix))
- m.prefix4 = append(m.prefix4, prefix)
- case 16:
- ip6 := ipv6{
- a: binary.BigEndian.Uint64(ip[0:8]),
- b: binary.BigEndian.Uint64(ip[8:16]),
- }
- ip6 = normalize6(ip6, prefix)
-
- m.ip6 = append(m.ip6, ip6)
- m.prefix6 = append(m.prefix6, prefix)
+ addr, ok := netip.AddrFromSlice(cidr.Ip)
+ if !ok {
+ return fmt.Errorf("error when loading GeoIP: invalid IP: %s", cidr.Ip)
+ }
+ err := m.cidrSet.AddIpCidr(netip.PrefixFrom(addr, int(cidr.Prefix)))
+ if err != nil {
+ return fmt.Errorf("error when loading GeoIP: %w", err)
}
}
+ m.cidrSet.Merge()
return nil
}
@@ -238,91 +146,13 @@ func (m *GeoIPMatcher) SetReverseMatch(isReverseMatch bool) {
m.reverseMatch = isReverseMatch
}
-func (m *GeoIPMatcher) match4(ip uint32) bool {
- if len(m.ip4) == 0 {
- return false
- }
-
- if ip < m.ip4[0] {
- return false
- }
-
- size := uint32(len(m.ip4))
- l := uint32(0)
- r := size
- for l < r {
- x := ((l + r) >> 1)
- if ip < m.ip4[x] {
- r = x
- continue
- }
-
- nip := normalize4(ip, m.prefix4[x])
- if nip == m.ip4[x] {
- return true
- }
-
- l = x + 1
- }
-
- return l > 0 && normalize4(ip, m.prefix4[l-1]) == m.ip4[l-1]
-}
-
-func less6(a ipv6, b ipv6) bool {
- return a.a < b.a || (a.a == b.a && a.b < b.b)
-}
-
-func (m *GeoIPMatcher) match6(ip ipv6) bool {
- if len(m.ip6) == 0 {
- return false
- }
-
- if less6(ip, m.ip6[0]) {
- return false
- }
-
- size := uint32(len(m.ip6))
- l := uint32(0)
- r := size
- for l < r {
- x := (l + r) / 2
- if less6(ip, m.ip6[x]) {
- r = x
- continue
- }
-
- if normalize6(ip, m.prefix6[x]) == m.ip6[x] {
- return true
- }
-
- l = x + 1
- }
-
- return l > 0 && normalize6(ip, m.prefix6[l-1]) == m.ip6[l-1]
-}
-
// Match returns true if the given ip is included by the GeoIP.
-func (m *GeoIPMatcher) Match(ip net.IP) bool {
- switch len(ip) {
- case 4:
- if m.reverseMatch {
- return !m.match4(binary.BigEndian.Uint32(ip))
- }
- return m.match4(binary.BigEndian.Uint32(ip))
- case 16:
- if m.reverseMatch {
- return !m.match6(ipv6{
- a: binary.BigEndian.Uint64(ip[0:8]),
- b: binary.BigEndian.Uint64(ip[8:16]),
- })
- }
- return m.match6(ipv6{
- a: binary.BigEndian.Uint64(ip[0:8]),
- b: binary.BigEndian.Uint64(ip[8:16]),
- })
- default:
- return false
+func (m *GeoIPMatcher) Match(ip netip.Addr) bool {
+ match := m.cidrSet.IsContain(ip)
+ if m.reverseMatch {
+ return !match
}
+ return match
}
// GeoIPMatcherContainer is a container for GeoIPMatchers. It keeps unique copies of GeoIPMatcher by country code.
@@ -344,6 +174,7 @@ func (c *GeoIPMatcherContainer) Add(geoip *GeoIP) (*GeoIPMatcher, error) {
m := &GeoIPMatcher{
countryCode: geoip.CountryCode,
reverseMatch: geoip.ReverseMatch,
+ cidrSet: cidr.NewIpCidrSet(),
}
if err := m.Init(geoip.Cidr); err != nil {
return nil, err
@@ -369,8 +200,7 @@ func NewGeoIPMatcher(geoip *GeoIP) (*GeoIPMatcher, error) {
return matcher, nil
}
-func (m *MultiGeoIPMatcher) ApplyIp(ip net.IP) bool {
-
+func (m *MultiGeoIPMatcher) ApplyIp(ip netip.Addr) bool {
for _, matcher := range m.matchers {
if matcher.Match(ip) {
return true
diff --git a/dns/filters.go b/dns/filters.go
index 46244c35..d8633e8b 100644
--- a/dns/filters.go
+++ b/dns/filters.go
@@ -41,7 +41,7 @@ func (gf *geoipFilter) Match(ip netip.Addr) bool {
return false
}
}
- return !geoIPMatcher.Match(ip.AsSlice())
+ return !geoIPMatcher.Match(ip)
}
type ipnetFilter struct {
diff --git a/rules/common/geoip.go b/rules/common/geoip.go
index 3a29fae4..ebca1d16 100644
--- a/rules/common/geoip.go
+++ b/rules/common/geoip.go
@@ -48,7 +48,7 @@ func (g *GEOIP) Match(metadata *C.Metadata) (bool, string) {
}
return false, g.adapter
}
- return g.geoIPMatcher.Match(ip.AsSlice()), g.adapter
+ return g.geoIPMatcher.Match(ip), g.adapter
}
func (g *GEOIP) Adapter() string {
From edf318bae04c013c9c0c762b2cca234157b126d3 Mon Sep 17 00:00:00 2001
From: wwqgtxx
Date: Sat, 13 Jan 2024 18:15:30 +0800
Subject: [PATCH 14/34] chore: better IPSet code
---
component/cidr/ipcidr_set.go | 44 +++++++++++++++++++--------
component/cidr/ipcidr_set_test.go | 4 +--
component/geodata/router/condition.go | 4 +--
3 files changed, 33 insertions(+), 19 deletions(-)
diff --git a/component/cidr/ipcidr_set.go b/component/cidr/ipcidr_set.go
index dac2d38a..0cb55e36 100644
--- a/component/cidr/ipcidr_set.go
+++ b/component/cidr/ipcidr_set.go
@@ -1,12 +1,16 @@
package cidr
import (
- "go4.org/netipx"
+ "fmt"
"net/netip"
+ "unsafe"
+
+ "go4.org/netipx"
)
type IpCidrSet struct {
- Ranges *netipx.IPSet
+ // must same with netipx.IPSet
+ rr []netipx.IPRange
}
func NewIpCidrSet() *IpCidrSet {
@@ -18,15 +22,15 @@ func (set *IpCidrSet) AddIpCidrForString(ipCidr string) error {
if err != nil {
return err
}
- err = set.AddIpCidr(prefix)
- return nil
+ return set.AddIpCidr(prefix)
}
func (set *IpCidrSet) AddIpCidr(ipCidr netip.Prefix) (err error) {
- var b netipx.IPSetBuilder
- b.AddSet(set.Ranges)
- b.AddPrefix(ipCidr)
- set.Ranges, err = b.IPSet()
+ if r := netipx.RangeOfPrefix(ipCidr); r.IsValid() {
+ set.rr = append(set.rr, r)
+ } else {
+ err = fmt.Errorf("not valid ipcidr range: %s", ipCidr)
+ }
return
}
@@ -39,10 +43,24 @@ func (set *IpCidrSet) IsContainForString(ipString string) bool {
}
func (set *IpCidrSet) IsContain(ip netip.Addr) bool {
- if set.Ranges == nil {
- return false
- }
- return set.Ranges.Contains(ip.WithZone(""))
+ return set.toIPSet().Contains(ip.WithZone(""))
}
-func (set *IpCidrSet) Merge() {}
+func (set *IpCidrSet) Merge() error {
+ var b netipx.IPSetBuilder
+ b.AddSet(set.toIPSet())
+ i, err := b.IPSet()
+ if err != nil {
+ return err
+ }
+ set.fromIPSet(i)
+ return nil
+}
+
+func (set *IpCidrSet) toIPSet() *netipx.IPSet {
+ return (*netipx.IPSet)(unsafe.Pointer(set))
+}
+
+func (set *IpCidrSet) fromIPSet(i *netipx.IPSet) {
+ *set = *(*IpCidrSet)(unsafe.Pointer(i))
+}
diff --git a/component/cidr/ipcidr_set_test.go b/component/cidr/ipcidr_set_test.go
index a6eaec84..b229aa2b 100644
--- a/component/cidr/ipcidr_set_test.go
+++ b/component/cidr/ipcidr_set_test.go
@@ -1,9 +1,7 @@
package cidr
import (
- "go4.org/netipx"
"testing"
- "unsafe"
)
func TestIpv4(t *testing.T) {
@@ -99,7 +97,7 @@ func TestMerge(t *testing.T) {
set.AddIpCidrForString(test.ipCidr2)
set.Merge()
- rangesLen := len(*(*[]netipx.IPRange)(unsafe.Pointer(set.Ranges)))
+ rangesLen := len(set.rr)
if rangesLen != test.expectedLen {
t.Errorf("Expected len: %v, got: %v", test.expectedLen, rangesLen)
diff --git a/component/geodata/router/condition.go b/component/geodata/router/condition.go
index c2ac8071..5261d2fe 100644
--- a/component/geodata/router/condition.go
+++ b/component/geodata/router/condition.go
@@ -137,9 +137,7 @@ func (m *GeoIPMatcher) Init(cidrs []*CIDR) error {
return fmt.Errorf("error when loading GeoIP: %w", err)
}
}
- m.cidrSet.Merge()
-
- return nil
+ return m.cidrSet.Merge()
}
func (m *GeoIPMatcher) SetReverseMatch(isReverseMatch bool) {
From 061f83d92a1d82227ef8063a126c2cb6839dadca Mon Sep 17 00:00:00 2001
From: Larvan2 <78135608+Larvan2@users.noreply.github.com>
Date: Wed, 17 Jan 2024 13:31:21 +0800
Subject: [PATCH 15/34] docs: README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 8c82536c..975f1268 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
-
+
From c34a0ef8f0f9a98f50912378d9c96d5edb65d927 Mon Sep 17 00:00:00 2001
From: Larvan2 <78135608+Larvan2@users.noreply.github.com>
Date: Wed, 17 Jan 2024 14:15:49 +0800
Subject: [PATCH 16/34] chore: trigger ci
---
rules/parser.go | 1 +
1 file changed, 1 insertion(+)
diff --git a/rules/parser.go b/rules/parser.go
index b1baa758..e2157cef 100644
--- a/rules/parser.go
+++ b/rules/parser.go
@@ -2,6 +2,7 @@ package rules
import (
"fmt"
+
C "github.com/metacubex/mihomo/constant"
RC "github.com/metacubex/mihomo/rules/common"
"github.com/metacubex/mihomo/rules/logic"
From 460cc240b074ac1adb7bb42c1c856253f673c900 Mon Sep 17 00:00:00 2001
From: riolurs
Date: Wed, 17 Jan 2024 16:13:55 +0800
Subject: [PATCH 17/34] fix(ntp): simplify NTP service initialization and error
handling
---
ntp/service.go | 34 +++++++++++++++++++---------------
1 file changed, 19 insertions(+), 15 deletions(-)
diff --git a/ntp/service.go b/ntp/service.go
index 4c95045a..d4582c99 100644
--- a/ntp/service.go
+++ b/ntp/service.go
@@ -47,8 +47,12 @@ func (srv *Service) Start() {
srv.mu.Lock()
defer srv.mu.Unlock()
log.Infoln("NTP service start, sync system time is %t", srv.syncSystemTime)
+ err := srv.update()
+ if err != nil {
+ log.Errorln("Initialize NTP time failed: %s", err)
+ return
+ }
service.running = true
- srv.update()
go srv.loopUpdate()
}
@@ -71,20 +75,16 @@ func (srv *Service) Running() bool {
return srv.running
}
-func (srv *Service) update() {
+func (srv *Service) update() error {
var response *ntp.Response
var err error
for i := 0; i < 3; i++ {
- response, err = ntp.Exchange(context.Background(), srv.dialer, srv.server)
- if err != nil {
- if i == 2 {
- log.Errorln("Initialize NTP time failed: %s", err)
- return
- }
- time.Sleep(time.Second * 2) // wait for 2 seconds before the next try
- continue
+ if response, err = ntp.Exchange(context.Background(), srv.dialer, srv.server); err == nil {
+ break
+ }
+ if i == 2 {
+ return err
}
- break
}
offset = response.ClockOffset
if offset > time.Duration(0) {
@@ -94,14 +94,15 @@ func (srv *Service) update() {
}
if srv.syncSystemTime {
timeNow := response.Time
- err = setSystemTime(timeNow)
- if err == nil {
+ syncErr := setSystemTime(timeNow)
+ if syncErr == nil {
log.Infoln("Sync system time success: %s", timeNow.Local().Format(ntp.TimeLayout))
} else {
- log.Errorln("Write time to system: %s", err)
+ log.Errorln("Write time to system: %s", syncErr)
srv.syncSystemTime = false
}
}
+ return nil
}
func (srv *Service) loopUpdate() {
@@ -111,7 +112,10 @@ func (srv *Service) loopUpdate() {
return
case <-srv.ticker.C:
}
- srv.update()
+ err := srv.update()
+ if err != nil {
+ log.Warnln("Sync time failed: %s", err)
+ }
}
}
From 36ea09eff4279bf8d1955cd7f4a42c16c975af38 Mon Sep 17 00:00:00 2001
From: snakem982
Date: Fri, 19 Jan 2024 21:20:11 +0800
Subject: [PATCH 18/34] fix: Converter SIP002 parameters parse (#976)
---
common/convert/converter.go | 33 ++++++++++++++++++++++++---------
1 file changed, 24 insertions(+), 9 deletions(-)
diff --git a/common/convert/converter.go b/common/convert/converter.go
index bf5bcbd7..809aa94f 100644
--- a/common/convert/converter.go
+++ b/common/convert/converter.go
@@ -68,7 +68,8 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
hysteria["skip-cert-verify"], _ = strconv.ParseBool(query.Get("insecure"))
proxies = append(proxies, hysteria)
- case "hysteria2":
+
+ case "hysteria2", "hy2":
urlHysteria2, err := url.Parse(line)
if err != nil {
continue
@@ -79,7 +80,7 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
hysteria2 := make(map[string]any, 20)
hysteria2["name"] = name
- hysteria2["type"] = scheme
+ hysteria2["type"] = "hysteria2"
hysteria2["server"] = urlHysteria2.Hostname()
if port := urlHysteria2.Port(); port != "" {
hysteria2["port"] = port
@@ -101,6 +102,7 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
hysteria2["up"] = query.Get("up")
proxies = append(proxies, hysteria2)
+
case "tuic":
// A temporary unofficial TUIC share link standard
// Modified from https://github.com/daeuniverse/dae/discussions/182
@@ -143,7 +145,7 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
}
proxies = append(proxies, tuic)
-
+
case "trojan":
urlTrojan, err := url.Parse(line)
if err != nil {
@@ -405,14 +407,27 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) {
if query.Get("udp-over-tcp") == "true" || query.Get("uot") == "1" {
ss["udp-over-tcp"] = true
}
- if strings.Contains(query.Get("plugin"), "obfs") {
- obfsParams := strings.Split(query.Get("plugin"), ";")
- ss["plugin"] = "obfs"
- ss["plugin-opts"] = map[string]any{
- "host": obfsParams[2][10:],
- "mode": obfsParams[1][5:],
+ plugin := query.Get("plugin")
+ if strings.Contains(plugin, ";") {
+ pluginInfo, _ := url.ParseQuery("pluginName=" + strings.ReplaceAll(plugin, ";", "&"))
+ pluginName := pluginInfo.Get("pluginName")
+ if strings.Contains(pluginName, "obfs") {
+ ss["plugin"] = "obfs"
+ ss["plugin-opts"] = map[string]any{
+ "mode": pluginInfo.Get("obfs"),
+ "host": pluginInfo.Get("obfs-host"),
+ }
+ } else if strings.Contains(pluginName, "v2ray-plugin") {
+ ss["plugin"] = "v2ray-plugin"
+ ss["plugin-opts"] = map[string]any{
+ "mode": pluginInfo.Get("mode"),
+ "host": pluginInfo.Get("host"),
+ "path": pluginInfo.Get("path"),
+ "tls": strings.Contains(plugin, "tls"),
+ }
}
}
+
proxies = append(proxies, ss)
case "ssr":
From 90ea6ab278894ae6e888ebef3dc3b50b5341a4bb Mon Sep 17 00:00:00 2001
From: wwqgtxx
Date: Sat, 20 Jan 2024 09:48:20 +0800
Subject: [PATCH 19/34] chore: update quic-go to 0.41.0
---
go.mod | 4 ++--
go.sum | 8 ++++----
transport/hysteria/core/client.go | 5 +++--
transport/tuic/v4/packet.go | 2 +-
transport/tuic/v5/packet.go | 6 +++---
5 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/go.mod b/go.mod
index f8252128..82f3ed07 100644
--- a/go.mod
+++ b/go.mod
@@ -20,8 +20,8 @@ require (
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40
github.com/mdlayher/netlink v1.7.2
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759
- github.com/metacubex/quic-go v0.40.1-0.20231130135418-0c1b47cf9394
- github.com/metacubex/sing-quic v0.0.0-20231220152840-85620b446796
+ github.com/metacubex/quic-go v0.41.1-0.20240120014142-a02f4a533d4a
+ github.com/metacubex/sing-quic v0.0.0-20240120014430-9838ce4bbc41
github.com/metacubex/sing-shadowsocks v0.2.6
github.com/metacubex/sing-shadowsocks2 v0.2.0
github.com/metacubex/sing-tun v0.2.0
diff --git a/go.sum b/go.sum
index c3209df8..7137932e 100644
--- a/go.sum
+++ b/go.sum
@@ -106,12 +106,12 @@ github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvO
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88=
github.com/metacubex/gvisor v0.0.0-20231209122014-3e43224c7bbc h1:+yTZ6q2EeQCAJNpKNEu5j32Pm23ShD38ElIa635wTrk=
github.com/metacubex/gvisor v0.0.0-20231209122014-3e43224c7bbc/go.mod h1:rhBU9tD5ktoGPBtXUquhWuGJ4u+8ZZzBMi2cAdv9q8Y=
-github.com/metacubex/quic-go v0.40.1-0.20231130135418-0c1b47cf9394 h1:dIT+KB2hknBCrwVAXPeY9tpzzkOZP5m40yqUteRT6/Y=
-github.com/metacubex/quic-go v0.40.1-0.20231130135418-0c1b47cf9394/go.mod h1:F/t8VnA47xoia8ABlNA4InkZjssvFJ5p6E6jKdbkgAs=
+github.com/metacubex/quic-go v0.41.1-0.20240120014142-a02f4a533d4a h1:IMr75VdMnDUhkANZemUWqmOPLfwnemiIaCHRnGCdAsY=
+github.com/metacubex/quic-go v0.41.1-0.20240120014142-a02f4a533d4a/go.mod h1:F/t8VnA47xoia8ABlNA4InkZjssvFJ5p6E6jKdbkgAs=
github.com/metacubex/sing v0.0.0-20240111014253-f1818b6a82b2 h1:upEO8dt9WDBavhgcgkXB3hRcwVNbkTbnd+xyzy6ZQZo=
github.com/metacubex/sing v0.0.0-20240111014253-f1818b6a82b2/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g=
-github.com/metacubex/sing-quic v0.0.0-20231220152840-85620b446796 h1:xiCPttMGAaIh4Ad6t85VxUoUv+Sg88eXzzUvYN8gT5w=
-github.com/metacubex/sing-quic v0.0.0-20231220152840-85620b446796/go.mod h1:E1e1Uu6YaJddD+c0DtJlSOkfMI0NLdOVhM60KAlcssY=
+github.com/metacubex/sing-quic v0.0.0-20240120014430-9838ce4bbc41 h1:nBo+cgprEu5f6vfJ2lpNvoUh13QUWR3oq1Bul9iF9HY=
+github.com/metacubex/sing-quic v0.0.0-20240120014430-9838ce4bbc41/go.mod h1:bdHqEysJclB9BzIa5jcKKSZ1qua+YEPjR8fOzzE3vZU=
github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ=
github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg=
github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A=
diff --git a/transport/hysteria/core/client.go b/transport/hysteria/core/client.go
index 258a0005..199fe0d4 100644
--- a/transport/hysteria/core/client.go
+++ b/transport/hysteria/core/client.go
@@ -402,10 +402,11 @@ func (c *quicPktConn) WriteTo(p []byte, addr string) error {
_ = struc.Pack(&msgBuf, &msg)
err = c.Session.SendDatagram(msgBuf.Bytes())
if err != nil {
- if errSize, ok := err.(quic.ErrMessageTooLarge); ok {
+ var errSize *quic.DatagramTooLargeError
+ if errors.As(err, &errSize) {
// need to frag
msg.MsgID = uint16(fastrand.Intn(0xFFFF)) + 1 // msgID must be > 0 when fragCount > 1
- fragMsgs := fragUDPMessage(msg, int(errSize))
+ fragMsgs := fragUDPMessage(msg, int(errSize.PeerMaxDatagramFrameSize))
for _, fragMsg := range fragMsgs {
msgBuf.Reset()
_ = struc.Pack(&msgBuf, &fragMsg)
diff --git a/transport/tuic/v4/packet.go b/transport/tuic/v4/packet.go
index f282b3ed..8f5bb5b3 100644
--- a/transport/tuic/v4/packet.go
+++ b/transport/tuic/v4/packet.go
@@ -123,7 +123,7 @@ func (q *quicStreamPacketConn) WaitReadFrom() (data []byte, put func(), addr net
func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
if q.udpRelayMode != common.QUIC && len(p) > q.maxUdpRelayPacketSize {
- return 0, quic.ErrMessageTooLarge(q.maxUdpRelayPacketSize)
+ return 0, &quic.DatagramTooLargeError{PeerMaxDatagramFrameSize: int64(q.maxUdpRelayPacketSize)}
}
if q.closed {
return 0, net.ErrClosed
diff --git a/transport/tuic/v5/packet.go b/transport/tuic/v5/packet.go
index a34e6a58..86f839a5 100644
--- a/transport/tuic/v5/packet.go
+++ b/transport/tuic/v5/packet.go
@@ -137,7 +137,7 @@ func (q *quicStreamPacketConn) WaitReadFrom() (data []byte, put func(), addr net
func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
if len(p) > 0xffff { // uint16 max
- return 0, quic.ErrMessageTooLarge(0xffff)
+ return 0, &quic.DatagramTooLargeError{PeerMaxDatagramFrameSize: 0xffff}
}
if q.closed {
return 0, net.ErrClosed
@@ -187,9 +187,9 @@ func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err erro
err = q.quicConn.SendDatagram(data)
}
- var tooLarge quic.ErrMessageTooLarge
+ var tooLarge *quic.DatagramTooLargeError
if errors.As(err, &tooLarge) {
- err = fragWriteNative(q.quicConn, packet, buf, int(tooLarge)-PacketOverHead)
+ err = fragWriteNative(q.quicConn, packet, buf, int(tooLarge.PeerMaxDatagramFrameSize)-PacketOverHead)
}
if err != nil {
return
From 25d6ad220d15aaedf6a07720f59845b3d3c725da Mon Sep 17 00:00:00 2001
From: pretze <105348431+pretzel0373@users.noreply.github.com>
Date: Sat, 20 Jan 2024 10:19:42 +0800
Subject: [PATCH 20/34] feat: add DSCP rule for Tproxy UDP packets (#996)
* feat: add `DSCP` rule for Tproxy UDP packets
* fix: fix compatibility issue with non_linux platform
* chore: remove redundant lines for DSCP
---
adapter/inbound/addition.go | 6 ++++
constant/metadata.go | 1 +
constant/rule.go | 3 ++
listener/tproxy/setsockopt_linux.go | 8 +++++
listener/tproxy/udp.go | 3 ++
listener/tproxy/udp_linux.go | 29 +++++++++++++++++-
listener/tproxy/udp_other.go | 4 +++
rules/common/dscp.go | 47 +++++++++++++++++++++++++++++
rules/parser.go | 2 ++
9 files changed, 102 insertions(+), 1 deletion(-)
create mode 100644 rules/common/dscp.go
diff --git a/adapter/inbound/addition.go b/adapter/inbound/addition.go
index a9896c8c..c38c1aa1 100644
--- a/adapter/inbound/addition.go
+++ b/adapter/inbound/addition.go
@@ -63,3 +63,9 @@ func WithInAddr(addr net.Addr) Addition {
}
}
}
+
+func WithDSCP(dscp uint8) Addition {
+ return func(metadata *C.Metadata) {
+ metadata.DSCP = dscp
+ }
+}
diff --git a/constant/metadata.go b/constant/metadata.go
index 3c712909..6df6ff43 100644
--- a/constant/metadata.go
+++ b/constant/metadata.go
@@ -147,6 +147,7 @@ type Metadata struct {
SpecialProxy string `json:"specialProxy"`
SpecialRules string `json:"specialRules"`
RemoteDst string `json:"remoteDestination"`
+ DSCP uint8 `json:"dscp"`
RawSrcAddr net.Addr `json:"-"`
RawDstAddr net.Addr `json:"-"`
diff --git a/constant/rule.go b/constant/rule.go
index 906f3cef..66fc18bb 100644
--- a/constant/rule.go
+++ b/constant/rule.go
@@ -14,6 +14,7 @@ const (
SrcPort
DstPort
InPort
+ DSCP
InUser
InName
InType
@@ -73,6 +74,8 @@ func (rt RuleType) String() string {
return "RuleSet"
case Network:
return "Network"
+ case DSCP:
+ return "DSCP"
case Uid:
return "Uid"
case SubRules:
diff --git a/listener/tproxy/setsockopt_linux.go b/listener/tproxy/setsockopt_linux.go
index 06f3e1c3..b83b28a4 100644
--- a/listener/tproxy/setsockopt_linux.go
+++ b/listener/tproxy/setsockopt_linux.go
@@ -34,6 +34,14 @@ func setsockopt(rc syscall.RawConn, addr string) error {
if err == nil && isIPv6 {
err = syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, IPV6_RECVORIGDSTADDR, 1)
}
+
+ if err == nil {
+ err = syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_RECVTOS, 1)
+ }
+
+ if err == nil {
+ err = syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, syscall.IPV6_RECVTCLASS, 1)
+ }
})
return err
diff --git a/listener/tproxy/udp.go b/listener/tproxy/udp.go
index aa0fee19..f738ef0d 100644
--- a/listener/tproxy/udp.go
+++ b/listener/tproxy/udp.go
@@ -79,6 +79,9 @@ func NewUDP(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*UDPLi
continue
}
+ dscp, _ := getDSCP(oob[:oobn])
+ additions = append(additions, inbound.WithDSCP(dscp))
+
if rAddr.Addr().Is4() {
// try to unmap 4in6 address
lAddr = netip.AddrPortFrom(lAddr.Addr().Unmap(), lAddr.Port())
diff --git a/listener/tproxy/udp_linux.go b/listener/tproxy/udp_linux.go
index 472a23d3..02b51379 100644
--- a/listener/tproxy/udp_linux.go
+++ b/listener/tproxy/udp_linux.go
@@ -104,7 +104,7 @@ func getOrigDst(oob []byte) (netip.AddrPort, error) {
}
// retrieve the destination address from the SCM.
- sa, err := unix.ParseOrigDstAddr(&scms[0])
+ sa, err := unix.ParseOrigDstAddr(&scms[1])
if err != nil {
return netip.AddrPort{}, fmt.Errorf("retrieve destination: %w", err)
}
@@ -122,3 +122,30 @@ func getOrigDst(oob []byte) (netip.AddrPort, error) {
return rAddr, nil
}
+
+func getDSCP (oob []byte) (uint8, error) {
+ scms, err := unix.ParseSocketControlMessage(oob)
+ if err != nil {
+ return 0, fmt.Errorf("parse control message: %w", err)
+ }
+ dscp, err := parseDSCP(&scms[0])
+ if err != nil {
+ return 0, fmt.Errorf("retrieve DSCP: %w", err)
+ }
+ return dscp, nil
+}
+
+func parseDSCP(m *unix.SocketControlMessage) (uint8, error) {
+ switch {
+ case m.Header.Level == unix.SOL_IP && m.Header.Type == unix.IP_TOS:
+ dscp := uint8(m.Data[0] >> 2)
+ return dscp, nil
+
+ case m.Header.Level == unix.SOL_IPV6 && m.Header.Type == unix.IPV6_TCLASS:
+ dscp := uint8(m.Data[0] >> 2)
+ return dscp, nil
+
+ default:
+ return 0, nil
+ }
+}
diff --git a/listener/tproxy/udp_other.go b/listener/tproxy/udp_other.go
index b35b07dd..2e0e0ae7 100644
--- a/listener/tproxy/udp_other.go
+++ b/listener/tproxy/udp_other.go
@@ -12,6 +12,10 @@ func getOrigDst(oob []byte) (netip.AddrPort, error) {
return netip.AddrPort{}, errors.New("UDP redir not supported on current platform")
}
+func getDSCP(oob []byte) (uint8, error) {
+ return 0, errors.New("UDP redir not supported on current platform")
+}
+
func dialUDP(network string, lAddr, rAddr netip.AddrPort) (*net.UDPConn, error) {
return nil, errors.New("UDP redir not supported on current platform")
}
diff --git a/rules/common/dscp.go b/rules/common/dscp.go
new file mode 100644
index 00000000..baedb28d
--- /dev/null
+++ b/rules/common/dscp.go
@@ -0,0 +1,47 @@
+package common
+
+import (
+ "fmt"
+ "strconv"
+
+ C "github.com/metacubex/mihomo/constant"
+)
+
+type DSCP struct {
+ *Base
+ dscp uint8
+ payload string
+ adapter string
+}
+
+func (d *DSCP) RuleType() C.RuleType {
+ return C.DSCP
+}
+
+func (d *DSCP) Match(metadata *C.Metadata) (bool, string) {
+ return metadata.DSCP == d.dscp, d.adapter
+}
+
+func (d *DSCP) Adapter() string {
+ return d.adapter
+}
+
+func (d *DSCP) Payload() string {
+ return d.payload
+}
+
+func NewDSCP(dscp string, adapter string) (*DSCP, error) {
+ dscpi, err := strconv.Atoi(dscp)
+ if err != nil {
+ return nil, fmt.Errorf("parse DSCP rule fail: %w", err)
+ }
+ if dscpi < 0 || dscpi > 63 {
+ return nil, fmt.Errorf("DSCP couldn't be negative or exceed 63")
+ }
+ return &DSCP{
+ Base: &Base{},
+ payload: dscp,
+ dscp: uint8(dscpi),
+ adapter: adapter,
+ }, nil
+}
diff --git a/rules/parser.go b/rules/parser.go
index e2157cef..7a79b18b 100644
--- a/rules/parser.go
+++ b/rules/parser.go
@@ -38,6 +38,8 @@ func ParseRule(tp, payload, target string, params []string, subRules map[string]
parsed, parseErr = RC.NewPort(payload, target, C.DstPort)
case "IN-PORT":
parsed, parseErr = RC.NewPort(payload, target, C.InPort)
+ case "DSCP":
+ parsed, parseErr = RC.NewDSCP(payload, target)
case "PROCESS-NAME":
parsed, parseErr = RC.NewProcess(payload, target, true)
case "PROCESS-PATH":
From 0e1bdb07d4e8bc45a0715f8b9a83d70de2b5a093 Mon Sep 17 00:00:00 2001
From: wwqgtxx
Date: Sat, 20 Jan 2024 11:00:06 +0800
Subject: [PATCH 21/34] chore: rewrite IntRanges constructor
---
adapter/outboundgroup/fallback.go | 2 +-
adapter/outboundgroup/parser.go | 2 +-
adapter/provider/parser.go | 2 +-
common/utils/ranges.go | 42 +++++++++++++++++++++++++++----
config/config.go | 4 +--
hub/route/groups.go | 2 +-
hub/route/proxies.go | 2 +-
rules/common/port.go | 2 +-
rules/common/uid.go | 2 +-
9 files changed, 46 insertions(+), 14 deletions(-)
diff --git a/adapter/outboundgroup/fallback.go b/adapter/outboundgroup/fallback.go
index 9db97cf3..4c8a2247 100644
--- a/adapter/outboundgroup/fallback.go
+++ b/adapter/outboundgroup/fallback.go
@@ -141,7 +141,7 @@ func (f *Fallback) Set(name string) error {
if !p.AliveForTestUrl(f.testUrl) {
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(5000))
defer cancel()
- expectedStatus, _ := utils.NewIntRanges[uint16](f.expectedStatus)
+ expectedStatus, _ := utils.NewUnsignedRanges[uint16](f.expectedStatus)
_, _ = p.URLTest(ctx, f.testUrl, expectedStatus)
}
diff --git a/adapter/outboundgroup/parser.go b/adapter/outboundgroup/parser.go
index de20c6ea..84b7ccd6 100644
--- a/adapter/outboundgroup/parser.go
+++ b/adapter/outboundgroup/parser.go
@@ -80,7 +80,7 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide
return nil, fmt.Errorf("%s: %w", groupName, errMissProxy)
}
- expectedStatus, err := utils.NewIntRanges[uint16](groupOption.ExpectedStatus)
+ expectedStatus, err := utils.NewUnsignedRanges[uint16](groupOption.ExpectedStatus)
if err != nil {
return nil, fmt.Errorf("%s: %w", groupName, err)
}
diff --git a/adapter/provider/parser.go b/adapter/provider/parser.go
index c78a9d39..9956dc8c 100644
--- a/adapter/provider/parser.go
+++ b/adapter/provider/parser.go
@@ -63,7 +63,7 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide
return nil, err
}
- expectedStatus, err := utils.NewIntRanges[uint16](schema.HealthCheck.ExpectedStatus)
+ expectedStatus, err := utils.NewUnsignedRanges[uint16](schema.HealthCheck.ExpectedStatus)
if err != nil {
return nil, err
}
diff --git a/common/utils/ranges.go b/common/utils/ranges.go
index f36c22ec..810105ff 100644
--- a/common/utils/ranges.go
+++ b/common/utils/ranges.go
@@ -13,7 +13,7 @@ type IntRanges[T constraints.Integer] []Range[T]
var errIntRanges = errors.New("intRanges error")
-func NewIntRanges[T constraints.Integer](expected string) (IntRanges[T], error) {
+func newIntRanges[T constraints.Integer](expected string, parseFn func(string) (T, error)) (IntRanges[T], error) {
// example: 200 or 200/302 or 200-400 or 200/204/401-429/501-503
expected = strings.TrimSpace(expected)
if len(expected) == 0 || expected == "*" {
@@ -25,10 +25,10 @@ func NewIntRanges[T constraints.Integer](expected string) (IntRanges[T], error)
return nil, fmt.Errorf("%w, too many ranges to use, maximum support 28 ranges", errIntRanges)
}
- return NewIntRangesFromList[T](list)
+ return newIntRangesFromList[T](list, parseFn)
}
-func NewIntRangesFromList[T constraints.Integer](list []string) (IntRanges[T], error) {
+func newIntRangesFromList[T constraints.Integer](list []string, parseFn func(string) (T, error)) (IntRanges[T], error) {
var ranges IntRanges[T]
for _, s := range list {
if s == "" {
@@ -41,7 +41,7 @@ func NewIntRangesFromList[T constraints.Integer](list []string) (IntRanges[T], e
return nil, errIntRanges
}
- start, err := strconv.ParseInt(strings.Trim(status[0], "[ ]"), 10, 64)
+ start, err := parseFn(strings.Trim(status[0], "[ ]"))
if err != nil {
return nil, errIntRanges
}
@@ -50,7 +50,7 @@ func NewIntRangesFromList[T constraints.Integer](list []string) (IntRanges[T], e
case 1:
ranges = append(ranges, NewRange(T(start), T(start)))
case 2:
- end, err := strconv.ParseUint(strings.Trim(status[1], "[ ]"), 10, 64)
+ end, err := parseFn(strings.Trim(status[1], "[ ]"))
if err != nil {
return nil, errIntRanges
}
@@ -62,6 +62,38 @@ func NewIntRangesFromList[T constraints.Integer](list []string) (IntRanges[T], e
return ranges, nil
}
+func parseUnsigned[T constraints.Unsigned](s string) (T, error) {
+ if val, err := strconv.ParseUint(s, 10, 64); err == nil {
+ return T(val), nil
+ } else {
+ return 0, err
+ }
+}
+
+func NewUnsignedRanges[T constraints.Unsigned](expected string) (IntRanges[T], error) {
+ return newIntRanges(expected, parseUnsigned[T])
+}
+
+func NewUnsignedRangesFromList[T constraints.Unsigned](list []string) (IntRanges[T], error) {
+ return newIntRangesFromList(list, parseUnsigned[T])
+}
+
+func parseSigned[T constraints.Signed](s string) (T, error) {
+ if val, err := strconv.ParseInt(s, 10, 64); err == nil {
+ return T(val), nil
+ } else {
+ return 0, err
+ }
+}
+
+func NewSignedRanges[T constraints.Signed](expected string) (IntRanges[T], error) {
+ return newIntRanges(expected, parseSigned[T])
+}
+
+func NewSignedRangesFromList[T constraints.Signed](list []string) (IntRanges[T], error) {
+ return newIntRangesFromList(list, parseSigned[T])
+}
+
func (ranges IntRanges[T]) Check(status T) bool {
if len(ranges) == 0 {
return true
diff --git a/config/config.go b/config/config.go
index bb503b49..dce61f63 100644
--- a/config/config.go
+++ b/config/config.go
@@ -1473,7 +1473,7 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) {
if len(snifferRaw.Sniff) != 0 {
for sniffType, sniffConfig := range snifferRaw.Sniff {
find := false
- ports, err := utils.NewIntRangesFromList[uint16](sniffConfig.Ports)
+ ports, err := utils.NewUnsignedRangesFromList[uint16](sniffConfig.Ports)
if err != nil {
return nil, err
}
@@ -1500,7 +1500,7 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) {
// Deprecated: Use Sniff instead
log.Warnln("Deprecated: Use Sniff instead")
}
- globalPorts, err := utils.NewIntRangesFromList[uint16](snifferRaw.Ports)
+ globalPorts, err := utils.NewUnsignedRangesFromList[uint16](snifferRaw.Ports)
if err != nil {
return nil, err
}
diff --git a/hub/route/groups.go b/hub/route/groups.go
index 18aabf74..30dec5f0 100644
--- a/hub/route/groups.go
+++ b/hub/route/groups.go
@@ -78,7 +78,7 @@ func getGroupDelay(w http.ResponseWriter, r *http.Request) {
return
}
- expectedStatus, err := utils.NewIntRanges[uint16](query.Get("expected"))
+ expectedStatus, err := utils.NewUnsignedRanges[uint16](query.Get("expected"))
if err != nil {
render.Status(r, http.StatusBadRequest)
render.JSON(w, r, ErrBadRequest)
diff --git a/hub/route/proxies.go b/hub/route/proxies.go
index 48359749..69c8e446 100644
--- a/hub/route/proxies.go
+++ b/hub/route/proxies.go
@@ -113,7 +113,7 @@ func getProxyDelay(w http.ResponseWriter, r *http.Request) {
return
}
- expectedStatus, err := utils.NewIntRanges[uint16](query.Get("expected"))
+ expectedStatus, err := utils.NewUnsignedRanges[uint16](query.Get("expected"))
if err != nil {
render.Status(r, http.StatusBadRequest)
render.JSON(w, r, ErrBadRequest)
diff --git a/rules/common/port.go b/rules/common/port.go
index ec76cf30..d3f6e1b4 100644
--- a/rules/common/port.go
+++ b/rules/common/port.go
@@ -39,7 +39,7 @@ func (p *Port) Payload() string {
}
func NewPort(port string, adapter string, ruleType C.RuleType) (*Port, error) {
- portRanges, err := utils.NewIntRanges[uint16](port)
+ portRanges, err := utils.NewUnsignedRanges[uint16](port)
if err != nil {
return nil, fmt.Errorf("%w, %w", errPayload, err)
}
diff --git a/rules/common/uid.go b/rules/common/uid.go
index de46c409..c80632b0 100644
--- a/rules/common/uid.go
+++ b/rules/common/uid.go
@@ -21,7 +21,7 @@ func NewUid(oUid, adapter string) (*Uid, error) {
return nil, fmt.Errorf("uid rule not support this platform")
}
- uidRange, err := utils.NewIntRanges[uint32](oUid)
+ uidRange, err := utils.NewUnsignedRanges[uint32](oUid)
if err != nil {
return nil, fmt.Errorf("%w, %w", errPayload, err)
}
From c1f0ed18ef82ec80cc309e973d98b20304226a29 Mon Sep 17 00:00:00 2001
From: wwqgtxx
Date: Sat, 20 Jan 2024 11:00:29 +0800
Subject: [PATCH 22/34] chore: dscp support range too
---
rules/common/dscp.go | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/rules/common/dscp.go b/rules/common/dscp.go
index baedb28d..c839b20d 100644
--- a/rules/common/dscp.go
+++ b/rules/common/dscp.go
@@ -2,14 +2,14 @@ package common
import (
"fmt"
- "strconv"
+ "github.com/metacubex/mihomo/common/utils"
C "github.com/metacubex/mihomo/constant"
)
type DSCP struct {
*Base
- dscp uint8
+ ranges utils.IntRanges[uint8]
payload string
adapter string
}
@@ -19,7 +19,7 @@ func (d *DSCP) RuleType() C.RuleType {
}
func (d *DSCP) Match(metadata *C.Metadata) (bool, string) {
- return metadata.DSCP == d.dscp, d.adapter
+ return d.ranges.Check(metadata.DSCP), d.adapter
}
func (d *DSCP) Adapter() string {
@@ -31,17 +31,19 @@ func (d *DSCP) Payload() string {
}
func NewDSCP(dscp string, adapter string) (*DSCP, error) {
- dscpi, err := strconv.Atoi(dscp)
+ ranges, err := utils.NewUnsignedRanges[uint8](dscp)
if err != nil {
return nil, fmt.Errorf("parse DSCP rule fail: %w", err)
}
- if dscpi < 0 || dscpi > 63 {
- return nil, fmt.Errorf("DSCP couldn't be negative or exceed 63")
+ for _, r := range ranges {
+ if r.End() > 63 {
+ return nil, fmt.Errorf("DSCP couldn't be negative or exceed 63")
+ }
}
return &DSCP{
Base: &Base{},
payload: dscp,
- dscp: uint8(dscpi),
+ ranges: ranges,
adapter: adapter,
}, nil
}
From e86567ead2579cef92b0408b979251f03fdf69f4 Mon Sep 17 00:00:00 2001
From: xishang0128
Date: Sat, 20 Jan 2024 19:43:10 +0800
Subject: [PATCH 23/34] chore: limit the default url
---
adapter/outboundgroup/parser.go | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/adapter/outboundgroup/parser.go b/adapter/outboundgroup/parser.go
index 84b7ccd6..6f44b8ea 100644
--- a/adapter/outboundgroup/parser.go
+++ b/adapter/outboundgroup/parser.go
@@ -92,9 +92,11 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide
groupOption.ExpectedStatus = status
testUrl := groupOption.URL
- if groupOption.URL == "" {
- groupOption.URL = C.DefaultTestURL
- testUrl = groupOption.URL
+ if groupOption.Type != "select" && groupOption.Type != "relay" {
+ if groupOption.URL == "" {
+ groupOption.URL = C.DefaultTestURL
+ testUrl = groupOption.URL
+ }
}
if len(GroupProxies) != 0 {
From 7be6751650f275140a6197265c2511f8f6654fe8 Mon Sep 17 00:00:00 2001
From: Larvan2 <78135608+Larvan2@users.noreply.github.com>
Date: Sun, 21 Jan 2024 16:57:21 +0800
Subject: [PATCH 24/34] fix: trigger-cmfa-update
---
.github/workflows/trigger-cmfa-update.yml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/trigger-cmfa-update.yml b/.github/workflows/trigger-cmfa-update.yml
index d767657e..d9523a07 100644
--- a/.github/workflows/trigger-cmfa-update.yml
+++ b/.github/workflows/trigger-cmfa-update.yml
@@ -15,7 +15,7 @@ on:
- Alpha
jobs:
- # Send "core-updated" to MetaCubeX/MihomoForAndroid to trigger update-dependencies
+ # Send "core-updated" to MetaCubeX/ClashMetaForAndroid to trigger update-dependencies
trigger-CMFA-update:
runs-on: ubuntu-latest
steps:
@@ -27,7 +27,7 @@ jobs:
- name: Trigger update-dependencies
run: |
- curl -X POST https://api.github.com/repos/MetaCubeX/MihomoForAndroid/dispatches \
+ curl -X POST https://api.github.com/repos/MetaCubeX/ClashMetaForAndroid/dispatches \
-H "Accept: application/vnd.github.everest-preview+json" \
-H "Authorization: token ${{ steps.generate-token.outputs.token }}" \
- -d '{"event_type": "core-updated"}'
\ No newline at end of file
+ -d '{"event_type": "core-updated"}'
From 5c1404f78ef5c9c03258d9404913d876805ba75e Mon Sep 17 00:00:00 2001
From: wwqgtxx
Date: Mon, 22 Jan 2024 20:56:28 +0800
Subject: [PATCH 25/34] chore: hysteria2 add `udp-mtu` option default value is
`1200-3` to match old version quic-go's capability
---
adapter/outbound/hysteria2.go | 8 ++++++++
go.mod | 2 +-
go.sum | 4 ++--
listener/config/hysteria2.go | 1 +
listener/inbound/hysteria2.go | 2 ++
listener/sing_hysteria2/server.go | 7 +++++++
transport/tuic/v5/frag.go | 4 +++-
7 files changed, 24 insertions(+), 4 deletions(-)
diff --git a/adapter/outbound/hysteria2.go b/adapter/outbound/hysteria2.go
index ddd5ccea..47272ec8 100644
--- a/adapter/outbound/hysteria2.go
+++ b/adapter/outbound/hysteria2.go
@@ -50,6 +50,7 @@ type Hysteria2Option struct {
CustomCA string `proxy:"ca,omitempty"`
CustomCAString string `proxy:"ca-str,omitempty"`
CWND int `proxy:"cwnd,omitempty"`
+ UdpMTU int `proxy:"udp-mtu,omitempty"`
}
func (h *Hysteria2) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) {
@@ -117,6 +118,12 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) {
tlsConfig.NextProtos = option.ALPN
}
+ if option.UdpMTU == 0 {
+ // "1200" from quic-go's MaxDatagramSize
+ // "-3" from quic-go's DatagramFrame.MaxDataLen
+ option.UdpMTU = 1200 - 3
+ }
+
singDialer := proxydialer.NewByNameSingDialer(option.DialerProxy, dialer.NewDialer())
clientOptions := hysteria2.ClientOptions{
@@ -130,6 +137,7 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) {
TLSConfig: tlsConfig,
UDPDisabled: false,
CWND: option.CWND,
+ UdpMTU: option.UdpMTU,
}
client, err := hysteria2.NewClient(clientOptions)
diff --git a/go.mod b/go.mod
index 82f3ed07..a0d5c447 100644
--- a/go.mod
+++ b/go.mod
@@ -21,7 +21,7 @@ require (
github.com/mdlayher/netlink v1.7.2
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759
github.com/metacubex/quic-go v0.41.1-0.20240120014142-a02f4a533d4a
- github.com/metacubex/sing-quic v0.0.0-20240120014430-9838ce4bbc41
+ github.com/metacubex/sing-quic v0.0.0-20240122125415-d6eb83bc6ec4
github.com/metacubex/sing-shadowsocks v0.2.6
github.com/metacubex/sing-shadowsocks2 v0.2.0
github.com/metacubex/sing-tun v0.2.0
diff --git a/go.sum b/go.sum
index 7137932e..c30bcbd9 100644
--- a/go.sum
+++ b/go.sum
@@ -110,8 +110,8 @@ github.com/metacubex/quic-go v0.41.1-0.20240120014142-a02f4a533d4a h1:IMr75VdMnD
github.com/metacubex/quic-go v0.41.1-0.20240120014142-a02f4a533d4a/go.mod h1:F/t8VnA47xoia8ABlNA4InkZjssvFJ5p6E6jKdbkgAs=
github.com/metacubex/sing v0.0.0-20240111014253-f1818b6a82b2 h1:upEO8dt9WDBavhgcgkXB3hRcwVNbkTbnd+xyzy6ZQZo=
github.com/metacubex/sing v0.0.0-20240111014253-f1818b6a82b2/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g=
-github.com/metacubex/sing-quic v0.0.0-20240120014430-9838ce4bbc41 h1:nBo+cgprEu5f6vfJ2lpNvoUh13QUWR3oq1Bul9iF9HY=
-github.com/metacubex/sing-quic v0.0.0-20240120014430-9838ce4bbc41/go.mod h1:bdHqEysJclB9BzIa5jcKKSZ1qua+YEPjR8fOzzE3vZU=
+github.com/metacubex/sing-quic v0.0.0-20240122125415-d6eb83bc6ec4 h1:4EukI/UdbuaXov7VDDZf4vaP0ZmkUu827DOeQqzVysw=
+github.com/metacubex/sing-quic v0.0.0-20240122125415-d6eb83bc6ec4/go.mod h1:bdHqEysJclB9BzIa5jcKKSZ1qua+YEPjR8fOzzE3vZU=
github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ=
github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg=
github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A=
diff --git a/listener/config/hysteria2.go b/listener/config/hysteria2.go
index 898204c6..3c9df508 100644
--- a/listener/config/hysteria2.go
+++ b/listener/config/hysteria2.go
@@ -21,6 +21,7 @@ type Hysteria2Server struct {
IgnoreClientBandwidth bool `yaml:"ignore-client-bandwidth" json:"ignore-client-bandwidth,omitempty"`
Masquerade string `yaml:"masquerade" json:"masquerade,omitempty"`
CWND int `yaml:"cwnd" json:"cwnd,omitempty"`
+ UdpMTU int `yaml:"udp-mtu" json:"udp-mtu,omitempty"`
MuxOption sing.MuxOption `yaml:"mux-option" json:"mux-option,omitempty"`
}
diff --git a/listener/inbound/hysteria2.go b/listener/inbound/hysteria2.go
index acd5f9a8..31c2c4b8 100644
--- a/listener/inbound/hysteria2.go
+++ b/listener/inbound/hysteria2.go
@@ -21,6 +21,7 @@ type Hysteria2Option struct {
IgnoreClientBandwidth bool `inbound:"ignore-client-bandwidth,omitempty"`
Masquerade string `inbound:"masquerade,omitempty"`
CWND int `inbound:"cwnd,omitempty"`
+ UdpMTU int `inbound:"udp-mtu,omitempty"`
MuxOption MuxOption `inbound:"mux-option,omitempty"`
}
@@ -58,6 +59,7 @@ func NewHysteria2(options *Hysteria2Option) (*Hysteria2, error) {
IgnoreClientBandwidth: options.IgnoreClientBandwidth,
Masquerade: options.Masquerade,
CWND: options.CWND,
+ UdpMTU: options.UdpMTU,
MuxOption: options.MuxOption.Build(),
},
}, nil
diff --git a/listener/sing_hysteria2/server.go b/listener/sing_hysteria2/server.go
index de4c95f5..77ad8efd 100644
--- a/listener/sing_hysteria2/server.go
+++ b/listener/sing_hysteria2/server.go
@@ -104,6 +104,12 @@ func New(config LC.Hysteria2Server, tunnel C.Tunnel, additions ...inbound.Additi
}
}
+ if config.UdpMTU == 0 {
+ // "1200" from quic-go's MaxDatagramSize
+ // "-3" from quic-go's DatagramFrame.MaxDataLen
+ config.UdpMTU = 1200 - 3
+ }
+
service, err := hysteria2.NewService[string](hysteria2.ServiceOptions{
Context: context.Background(),
Logger: log.SingLogger,
@@ -115,6 +121,7 @@ func New(config LC.Hysteria2Server, tunnel C.Tunnel, additions ...inbound.Additi
Handler: h,
MasqueradeHandler: masqueradeHandler,
CWND: config.CWND,
+ UdpMTU: config.UdpMTU,
})
if err != nil {
return nil, err
diff --git a/transport/tuic/v5/frag.go b/transport/tuic/v5/frag.go
index b0e1a174..d680a5d0 100644
--- a/transport/tuic/v5/frag.go
+++ b/transport/tuic/v5/frag.go
@@ -12,7 +12,9 @@ import (
// MaxFragSize is a safe udp relay packet size
// because tuicv5 support udp fragment so we unneeded to do a magic modify for quic-go to increase MaxDatagramFrameSize
// it may not work fine in some platform
-var MaxFragSize = 1200 - PacketOverHead
+// "1200" from quic-go's MaxDatagramSize
+// "-3" from quic-go's DatagramFrame.MaxDataLen
+var MaxFragSize = 1200 - PacketOverHead - 3
func fragWriteNative(quicConn quic.Connection, packet Packet, buf *bytes.Buffer, fragSize int) (err error) {
fullPayload := packet.DATA
From 5858384631c454b03c7a359a6db9819b23ec2a15 Mon Sep 17 00:00:00 2001
From: xishang0128
Date: Wed, 24 Jan 2024 11:33:37 +0800
Subject: [PATCH 26/34] chore: modify initial resource update
---
component/resource/fetcher.go | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/component/resource/fetcher.go b/component/resource/fetcher.go
index 44ca45c2..e6291293 100644
--- a/component/resource/fetcher.go
+++ b/component/resource/fetcher.go
@@ -13,10 +13,6 @@ import (
"github.com/samber/lo"
)
-const (
- minInterval = time.Minute * 5
-)
-
var (
fileMode os.FileMode = 0o666
dirMode os.FileMode = 0o755
@@ -164,8 +160,8 @@ func (f *Fetcher[V]) Destroy() error {
func (f *Fetcher[V]) pullLoop() {
initialInterval := f.interval - time.Since(f.UpdatedAt)
- if initialInterval < minInterval {
- initialInterval = minInterval
+ if initialInterval > f.interval {
+ initialInterval = f.interval
}
timer := time.NewTimer(initialInterval)
From 80d6ca8ef96eb3a00dfb3c712d384016c392ed20 Mon Sep 17 00:00:00 2001
From: wwqgtxx
Date: Wed, 24 Jan 2024 11:54:07 +0800
Subject: [PATCH 27/34] fix: h2mux udp not working
---
go.mod | 2 +-
go.sum | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/go.mod b/go.mod
index a0d5c447..6f598042 100644
--- a/go.mod
+++ b/go.mod
@@ -35,7 +35,7 @@ require (
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a
github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97
github.com/sagernet/sing v0.3.0
- github.com/sagernet/sing-mux v0.2.0
+ github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6
github.com/sagernet/sing-shadowtls v0.1.4
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6
github.com/sagernet/utls v1.5.4
diff --git a/go.sum b/go.sum
index c30bcbd9..bb942b7f 100644
--- a/go.sum
+++ b/go.sum
@@ -157,8 +157,8 @@ github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkk
github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM=
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-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo=
-github.com/sagernet/sing-mux v0.2.0/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ=
+github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 h1:5bCAkvDDzSMITiHFjolBwpdqYsvycdTu71FsMEFXQ14=
+github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ=
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
From 1025101954133aca16753f020d2b3d0e0ff70d84 Mon Sep 17 00:00:00 2001
From: xishang0128
Date: Wed, 24 Jan 2024 12:45:35 +0800
Subject: [PATCH 28/34] chore: add `timeout` option
---
adapter/outboundgroup/parser.go | 3 ++-
adapter/provider/healthcheck.go | 13 +++++++------
adapter/provider/parser.go | 3 ++-
config/config.go | 2 +-
4 files changed, 12 insertions(+), 9 deletions(-)
diff --git a/adapter/outboundgroup/parser.go b/adapter/outboundgroup/parser.go
index 6f44b8ea..959041dd 100644
--- a/adapter/outboundgroup/parser.go
+++ b/adapter/outboundgroup/parser.go
@@ -28,6 +28,7 @@ type GroupCommonOption struct {
Use []string `group:"use,omitempty"`
URL string `group:"url,omitempty"`
Interval int `group:"interval,omitempty"`
+ TestTimeout int `group:"timeout,omitempty"`
Lazy bool `group:"lazy,omitempty"`
DisableUDP bool `group:"disable-udp,omitempty"`
Filter string `group:"filter,omitempty"`
@@ -116,7 +117,7 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide
}
}
- hc := provider.NewHealthCheck(ps, testUrl, uint(groupOption.Interval), groupOption.Lazy, expectedStatus)
+ hc := provider.NewHealthCheck(ps, testUrl, uint(groupOption.TestTimeout), uint(groupOption.Interval), groupOption.Lazy, expectedStatus)
pd, err := provider.NewCompatibleProvider(groupName, ps, hc)
if err != nil {
diff --git a/adapter/provider/healthcheck.go b/adapter/provider/healthcheck.go
index 12e1df42..fbc1d14f 100644
--- a/adapter/provider/healthcheck.go
+++ b/adapter/provider/healthcheck.go
@@ -16,10 +16,6 @@ import (
"github.com/dlclark/regexp2"
)
-const (
- defaultURLTestTimeout = time.Second * 5
-)
-
type HealthCheckOption struct {
URL string
Interval uint
@@ -42,6 +38,7 @@ type HealthCheck struct {
lastTouch atomic.TypedValue[time.Time]
done chan struct{}
singleDo *singledo.Single[struct{}]
+ timeout time.Duration
}
func (hc *HealthCheck) process() {
@@ -198,7 +195,7 @@ func (hc *HealthCheck) execute(b *batch.Batch[bool], url, uid string, option *ex
p := proxy
b.Go(p.Name(), func() (bool, error) {
- ctx, cancel := context.WithTimeout(context.Background(), defaultURLTestTimeout)
+ ctx, cancel := context.WithTimeout(context.Background(), hc.timeout)
defer cancel()
log.Debugln("Health Checking, proxy: %s, url: %s, id: {%s}", p.Name(), url, uid)
_, _ = p.URLTest(ctx, url, expectedStatus)
@@ -212,15 +209,19 @@ func (hc *HealthCheck) close() {
hc.done <- struct{}{}
}
-func NewHealthCheck(proxies []C.Proxy, url string, interval uint, lazy bool, expectedStatus utils.IntRanges[uint16]) *HealthCheck {
+func NewHealthCheck(proxies []C.Proxy, url string, timeout uint, interval uint, lazy bool, expectedStatus utils.IntRanges[uint16]) *HealthCheck {
if url == "" {
// expectedStatus = nil
url = C.DefaultTestURL
}
+ if timeout == 0 {
+ timeout = 5000
+ }
return &HealthCheck{
proxies: proxies,
url: url,
+ timeout: time.Duration(timeout) * time.Millisecond,
extra: map[string]*extraOption{},
interval: time.Duration(interval) * time.Second,
lazy: lazy,
diff --git a/adapter/provider/parser.go b/adapter/provider/parser.go
index 9956dc8c..88b36b7c 100644
--- a/adapter/provider/parser.go
+++ b/adapter/provider/parser.go
@@ -22,6 +22,7 @@ type healthCheckSchema struct {
Enable bool `provider:"enable"`
URL string `provider:"url"`
Interval int `provider:"interval"`
+ TestTimeout int `provider:"timeout,omitempty"`
Lazy bool `provider:"lazy,omitempty"`
ExpectedStatus string `provider:"expected-status,omitempty"`
}
@@ -75,7 +76,7 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide
}
hcInterval = uint(schema.HealthCheck.Interval)
}
- hc := NewHealthCheck([]C.Proxy{}, schema.HealthCheck.URL, hcInterval, schema.HealthCheck.Lazy, expectedStatus)
+ hc := NewHealthCheck([]C.Proxy{}, schema.HealthCheck.URL, uint(schema.HealthCheck.TestTimeout), hcInterval, schema.HealthCheck.Lazy, expectedStatus)
var vehicle types.Vehicle
switch schema.Type {
diff --git a/config/config.go b/config/config.go
index dce61f63..b0401596 100644
--- a/config/config.go
+++ b/config/config.go
@@ -775,7 +775,7 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[
}
ps = append(ps, proxies[v])
}
- hc := provider.NewHealthCheck(ps, "", 0, true, nil)
+ hc := provider.NewHealthCheck(ps, "", 5000, 0, true, nil)
pd, _ := provider.NewCompatibleProvider(provider.ReservedName, ps, hc)
providersMap[provider.ReservedName] = pd
From 9bd70e1366e30a1703bd8ae3e65d9baafe24055f Mon Sep 17 00:00:00 2001
From: wwqgtxx
Date: Wed, 24 Jan 2024 17:52:45 +0800
Subject: [PATCH 29/34] fix: tfo not working with smux/yamux
---
component/dialer/dialer.go | 5 +++++
component/dialer/tfo.go | 9 +++++----
constant/adapters.go | 4 ++--
3 files changed, 12 insertions(+), 6 deletions(-)
diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go
index 985e2195..070d98b8 100644
--- a/component/dialer/dialer.go
+++ b/component/dialer/dialer.go
@@ -15,6 +15,11 @@ import (
"github.com/metacubex/mihomo/constant/features"
)
+const (
+ DefaultTCPTimeout = 5 * time.Second
+ DefaultUDPTimeout = DefaultTCPTimeout
+)
+
type dialFunc func(ctx context.Context, network string, ips []netip.Addr, port string, opt *option) (net.Conn, error)
var (
diff --git a/component/dialer/tfo.go b/component/dialer/tfo.go
index 4863d6ae..950bdfe4 100644
--- a/component/dialer/tfo.go
+++ b/component/dialer/tfo.go
@@ -2,10 +2,11 @@ package dialer
import (
"context"
- "github.com/sagernet/tfo-go"
"io"
"net"
"time"
+
+ "github.com/sagernet/tfo-go"
)
type tfoConn struct {
@@ -66,14 +67,14 @@ func (c *tfoConn) Close() error {
func (c *tfoConn) LocalAddr() net.Addr {
if c.Conn == nil {
- return nil
+ return &net.TCPAddr{}
}
return c.Conn.LocalAddr()
}
func (c *tfoConn) RemoteAddr() net.Addr {
if c.Conn == nil {
- return nil
+ return &net.TCPAddr{}
}
return c.Conn.RemoteAddr()
}
@@ -123,7 +124,7 @@ func (c *tfoConn) WriterReplaceable() bool {
}
func dialTFO(ctx context.Context, netDialer net.Dialer, network, address string) (net.Conn, error) {
- ctx, cancel := context.WithCancel(ctx)
+ ctx, cancel := context.WithTimeout(context.Background(), DefaultTCPTimeout)
dialer := tfo.Dialer{Dialer: netDialer, DisableTFO: false}
return &tfoConn{
dialed: make(chan bool, 1),
diff --git a/constant/adapters.go b/constant/adapters.go
index 9752de55..83c8223a 100644
--- a/constant/adapters.go
+++ b/constant/adapters.go
@@ -43,9 +43,9 @@ const (
)
const (
- DefaultTCPTimeout = 5 * time.Second
+ DefaultTCPTimeout = dialer.DefaultTCPTimeout
+ DefaultUDPTimeout = dialer.DefaultUDPTimeout
DefaultDropTime = 12 * DefaultTCPTimeout
- DefaultUDPTimeout = DefaultTCPTimeout
DefaultTLSTimeout = DefaultTCPTimeout
DefaultTestURL = "https://www.gstatic.com/generate_204"
)
From f2634250a66149fabec09ab917300fc1aeeabf83 Mon Sep 17 00:00:00 2001
From: wwqgtxx
Date: Tue, 30 Jan 2024 12:11:05 +0800
Subject: [PATCH 30/34] fix: hy2's rawConn not closed
---
go.mod | 2 +-
go.sum | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/go.mod b/go.mod
index 6f598042..6d68966e 100644
--- a/go.mod
+++ b/go.mod
@@ -21,7 +21,7 @@ require (
github.com/mdlayher/netlink v1.7.2
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759
github.com/metacubex/quic-go v0.41.1-0.20240120014142-a02f4a533d4a
- github.com/metacubex/sing-quic v0.0.0-20240122125415-d6eb83bc6ec4
+ github.com/metacubex/sing-quic v0.0.0-20240130040922-cbe613c88f20
github.com/metacubex/sing-shadowsocks v0.2.6
github.com/metacubex/sing-shadowsocks2 v0.2.0
github.com/metacubex/sing-tun v0.2.0
diff --git a/go.sum b/go.sum
index bb942b7f..89a32116 100644
--- a/go.sum
+++ b/go.sum
@@ -110,8 +110,8 @@ github.com/metacubex/quic-go v0.41.1-0.20240120014142-a02f4a533d4a h1:IMr75VdMnD
github.com/metacubex/quic-go v0.41.1-0.20240120014142-a02f4a533d4a/go.mod h1:F/t8VnA47xoia8ABlNA4InkZjssvFJ5p6E6jKdbkgAs=
github.com/metacubex/sing v0.0.0-20240111014253-f1818b6a82b2 h1:upEO8dt9WDBavhgcgkXB3hRcwVNbkTbnd+xyzy6ZQZo=
github.com/metacubex/sing v0.0.0-20240111014253-f1818b6a82b2/go.mod h1:9pfuAH6mZfgnz/YjP6xu5sxx882rfyjpcrTdUpd6w3g=
-github.com/metacubex/sing-quic v0.0.0-20240122125415-d6eb83bc6ec4 h1:4EukI/UdbuaXov7VDDZf4vaP0ZmkUu827DOeQqzVysw=
-github.com/metacubex/sing-quic v0.0.0-20240122125415-d6eb83bc6ec4/go.mod h1:bdHqEysJclB9BzIa5jcKKSZ1qua+YEPjR8fOzzE3vZU=
+github.com/metacubex/sing-quic v0.0.0-20240130040922-cbe613c88f20 h1:wt7ydRxm9Pvw+un6KD97tjLJHMrkzp83HyiGkoz6e7k=
+github.com/metacubex/sing-quic v0.0.0-20240130040922-cbe613c88f20/go.mod h1:bdHqEysJclB9BzIa5jcKKSZ1qua+YEPjR8fOzzE3vZU=
github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ=
github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg=
github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A=
From c6843d6a2cea8fe777132a1641912994668418fa Mon Sep 17 00:00:00 2001
From: wwqgtxx
Date: Tue, 30 Jan 2024 12:28:52 +0800
Subject: [PATCH 31/34] fix: exclude loopback on darwin
---
go.mod | 2 +-
go.sum | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/go.mod b/go.mod
index 6d68966e..c2571ca2 100644
--- a/go.mod
+++ b/go.mod
@@ -24,7 +24,7 @@ require (
github.com/metacubex/sing-quic v0.0.0-20240130040922-cbe613c88f20
github.com/metacubex/sing-shadowsocks v0.2.6
github.com/metacubex/sing-shadowsocks2 v0.2.0
- github.com/metacubex/sing-tun v0.2.0
+ github.com/metacubex/sing-tun v0.2.1-0.20240130042529-1f983547e9d4
github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f
github.com/metacubex/sing-wireguard v0.0.0-20231209125515-0594297f7232
github.com/miekg/dns v1.1.57
diff --git a/go.sum b/go.sum
index 89a32116..8b821f53 100644
--- a/go.sum
+++ b/go.sum
@@ -116,8 +116,8 @@ github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwV
github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg=
github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A=
github.com/metacubex/sing-shadowsocks2 v0.2.0/go.mod h1:LCKF6j1P94zN8ZS+LXRK1gmYTVGB3squivBSXAFnOg8=
-github.com/metacubex/sing-tun v0.2.0 h1:CrWgFI58umlbrYa8RSygAMQ37dESpIX0z/Ruj6+2dZY=
-github.com/metacubex/sing-tun v0.2.0/go.mod h1:P+TjrGTG5AdQRaskP6NiI9gZmgnwR3o5ze9CkIQE+/s=
+github.com/metacubex/sing-tun v0.2.1-0.20240130042529-1f983547e9d4 h1:qz256cI4oGBtLT0H3wQYgazLGYLQUEZqMkf0i8sGH5A=
+github.com/metacubex/sing-tun v0.2.1-0.20240130042529-1f983547e9d4/go.mod h1:P+TjrGTG5AdQRaskP6NiI9gZmgnwR3o5ze9CkIQE+/s=
github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f h1:QjXrHKbTMBip/C+R79bvbfr42xH1gZl3uFb0RELdZiQ=
github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f/go.mod h1:olVkD4FChQ5gKMHG4ZzuD7+fMkJY1G8vwOKpRehjrmY=
github.com/metacubex/sing-wireguard v0.0.0-20231209125515-0594297f7232 h1:loWjR+k9dxqBSgruGyT5hE8UCRMmCEjxqZbryfY9no4=
From d1337f39ed35ce13be3053b95410305955000b77 Mon Sep 17 00:00:00 2001
From: wwqgtxx
Date: Tue, 30 Jan 2024 15:29:48 +0800
Subject: [PATCH 32/34] chore: slowdown wireguard dial retry
---
adapter/outbound/wireguard.go | 57 ++++++++++++++++++++++++++++++++++-
1 file changed, 56 insertions(+), 1 deletion(-)
diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go
index 9af1751b..19875b8c 100644
--- a/adapter/outbound/wireguard.go
+++ b/adapter/outbound/wireguard.go
@@ -12,7 +12,9 @@ import (
"strconv"
"strings"
"sync"
+ "time"
+ "github.com/metacubex/mihomo/common/atomic"
CN "github.com/metacubex/mihomo/common/net"
"github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/proxydialer"
@@ -23,6 +25,8 @@ import (
wireguard "github.com/metacubex/sing-wireguard"
+ "github.com/jpillora/backoff"
+
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/debug"
E "github.com/sagernet/sing/common/exceptions"
@@ -125,6 +129,48 @@ func (option WireGuardPeerOption) Prefixes() ([]netip.Prefix, error) {
return localPrefixes, nil
}
+type wgSingDialer struct {
+ proxydialer.SingDialer
+ errTimes atomic.Int64
+ backoff *backoff.Backoff
+}
+
+func (d *wgSingDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
+ if d.errTimes.Load() > 10 {
+ select {
+ case <-time.After(d.backoff.Duration()):
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ }
+ }
+ c, err := d.SingDialer.DialContext(ctx, network, destination)
+ if err != nil {
+ d.errTimes.Add(1)
+ return nil, err
+ }
+ d.errTimes.Store(0)
+ d.backoff.Reset()
+ return c, nil
+}
+
+func (d *wgSingDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
+ if d.errTimes.Load() > 10 {
+ select {
+ case <-time.After(d.backoff.Duration()):
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ }
+ }
+ c, err := d.SingDialer.ListenPacket(ctx, destination)
+ if err != nil {
+ d.errTimes.Add(1)
+ return nil, err
+ }
+ d.errTimes.Store(0)
+ d.backoff.Reset()
+ return c, nil
+}
+
func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
outbound := &WireGuard{
Base: &Base{
@@ -136,7 +182,16 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),
},
- dialer: proxydialer.NewByNameSingDialer(option.DialerProxy, dialer.NewDialer()),
+ dialer: &wgSingDialer{
+ SingDialer: proxydialer.NewByNameSingDialer(option.DialerProxy, dialer.NewDialer()),
+ errTimes: atomic.NewInt64(0),
+ backoff: &backoff.Backoff{
+ Min: 10 * time.Millisecond,
+ Max: 1 * time.Second,
+ Factor: 2,
+ Jitter: true,
+ },
+ },
}
runtime.SetFinalizer(outbound, closeWireGuard)
From 947ad9b308ed2d6a6ccd7fda2b50ddab16ebe3b1 Mon Sep 17 00:00:00 2001
From: tommy <150410121+tomcxx@users.noreply.github.com>
Date: Tue, 30 Jan 2024 15:51:12 +0800
Subject: [PATCH 33/34] chore: store latency data more reasonably (#964)
---
adapter/adapter.go | 15 ++++++++-------
docs/config.yaml | 4 +++-
2 files changed, 11 insertions(+), 8 deletions(-)
diff --git a/adapter/adapter.go b/adapter/adapter.go
index da6171f1..dbf3db6e 100644
--- a/adapter/adapter.go
+++ b/adapter/adapter.go
@@ -3,7 +3,6 @@ package adapter
import (
"context"
"encoding/json"
- "errors"
"fmt"
"net"
"net/http"
@@ -163,6 +162,8 @@ func (p *Proxy) MarshalJSON() ([]byte, error) {
// URLTest get the delay for the specified URL
// implements C.Proxy
func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16]) (t uint16, err error) {
+ var satisfied bool
+
defer func() {
alive := err == nil
record := C.DelayHistory{Time: time.Now()}
@@ -185,6 +186,11 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In
p.extra.Store(url, state)
}
+ if !satisfied {
+ record.Delay = 0
+ alive = false
+ }
+
state.alive.Store(alive)
state.history.Put(record)
if state.history.Len() > defaultHistoriesNum {
@@ -253,15 +259,10 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In
}
}
- if expectedStatus != nil && !expectedStatus.Check(uint16(resp.StatusCode)) {
- // maybe another value should be returned for differentiation
- err = errors.New("response status is inconsistent with the expected status")
- }
-
+ satisfied = resp != nil && (expectedStatus == nil || expectedStatus.Check(uint16(resp.StatusCode)))
t = uint16(time.Since(start) / time.Millisecond)
return
}
-
func NewProxy(adapter C.ProxyAdapter) *Proxy {
return &Proxy{
ProxyAdapter: adapter,
diff --git a/docs/config.yaml b/docs/config.yaml
index 2a44a993..eee6122b 100644
--- a/docs/config.yaml
+++ b/docs/config.yaml
@@ -788,6 +788,7 @@ proxy-groups:
- vmess1
# tolerance: 150
# lazy: true
+ # expected-status: 204 # 当健康检查返回状态码与期望值不符时,认为节点不可用
url: "https://cp.cloudflare.com/generate_204"
interval: 300
@@ -851,6 +852,7 @@ proxy-providers:
interval: 600
# lazy: true
url: https://cp.cloudflare.com/generate_204
+ # expected-status: 204 # 当健康检查返回状态码与期望值不符时,认为节点不可用
override: # 覆写节点加载时的一些配置项
skip-cert-verify: true
udp: true
@@ -1068,4 +1070,4 @@ listeners:
# authentication-timeout: 1000
# alpn:
# - h3
-# max-udp-relay-packet-size: 1500
+# max-udp-relay-packet-size: 1500
\ No newline at end of file
From e6011301b2ff85f550d646599a799d40e535eefe Mon Sep 17 00:00:00 2001
From: wwqgtxx
Date: Tue, 30 Jan 2024 19:41:34 +0800
Subject: [PATCH 34/34] chore: rebuild slowdown code
---
adapter/outbound/wireguard.go | 58 +-------------
component/proxydialer/slowdown.go | 34 +++++++++
component/proxydialer/slowdown_sing.go | 33 ++++++++
component/slowdown/backoff.go | 101 +++++++++++++++++++++++++
component/slowdown/slowdown.go | 49 ++++++++++++
go.mod | 1 -
go.sum | 2 -
tunnel/tunnel.go | 15 +---
8 files changed, 223 insertions(+), 70 deletions(-)
create mode 100644 component/proxydialer/slowdown.go
create mode 100644 component/proxydialer/slowdown_sing.go
create mode 100644 component/slowdown/backoff.go
create mode 100644 component/slowdown/slowdown.go
diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go
index 19875b8c..7c021c87 100644
--- a/adapter/outbound/wireguard.go
+++ b/adapter/outbound/wireguard.go
@@ -12,21 +12,18 @@ import (
"strconv"
"strings"
"sync"
- "time"
- "github.com/metacubex/mihomo/common/atomic"
CN "github.com/metacubex/mihomo/common/net"
"github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/proxydialer"
"github.com/metacubex/mihomo/component/resolver"
+ "github.com/metacubex/mihomo/component/slowdown"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/dns"
"github.com/metacubex/mihomo/log"
wireguard "github.com/metacubex/sing-wireguard"
- "github.com/jpillora/backoff"
-
"github.com/sagernet/sing/common"
"github.com/sagernet/sing/common/debug"
E "github.com/sagernet/sing/common/exceptions"
@@ -129,48 +126,6 @@ func (option WireGuardPeerOption) Prefixes() ([]netip.Prefix, error) {
return localPrefixes, nil
}
-type wgSingDialer struct {
- proxydialer.SingDialer
- errTimes atomic.Int64
- backoff *backoff.Backoff
-}
-
-func (d *wgSingDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
- if d.errTimes.Load() > 10 {
- select {
- case <-time.After(d.backoff.Duration()):
- case <-ctx.Done():
- return nil, ctx.Err()
- }
- }
- c, err := d.SingDialer.DialContext(ctx, network, destination)
- if err != nil {
- d.errTimes.Add(1)
- return nil, err
- }
- d.errTimes.Store(0)
- d.backoff.Reset()
- return c, nil
-}
-
-func (d *wgSingDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
- if d.errTimes.Load() > 10 {
- select {
- case <-time.After(d.backoff.Duration()):
- case <-ctx.Done():
- return nil, ctx.Err()
- }
- }
- c, err := d.SingDialer.ListenPacket(ctx, destination)
- if err != nil {
- d.errTimes.Add(1)
- return nil, err
- }
- d.errTimes.Store(0)
- d.backoff.Reset()
- return c, nil
-}
-
func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
outbound := &WireGuard{
Base: &Base{
@@ -182,16 +137,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) {
rmark: option.RoutingMark,
prefer: C.NewDNSPrefer(option.IPVersion),
},
- dialer: &wgSingDialer{
- SingDialer: proxydialer.NewByNameSingDialer(option.DialerProxy, dialer.NewDialer()),
- errTimes: atomic.NewInt64(0),
- backoff: &backoff.Backoff{
- Min: 10 * time.Millisecond,
- Max: 1 * time.Second,
- Factor: 2,
- Jitter: true,
- },
- },
+ dialer: proxydialer.NewSlowDownSingDialer(proxydialer.NewByNameSingDialer(option.DialerProxy, dialer.NewDialer()), slowdown.New()),
}
runtime.SetFinalizer(outbound, closeWireGuard)
diff --git a/component/proxydialer/slowdown.go b/component/proxydialer/slowdown.go
new file mode 100644
index 00000000..c62fc344
--- /dev/null
+++ b/component/proxydialer/slowdown.go
@@ -0,0 +1,34 @@
+package proxydialer
+
+import (
+ "context"
+ "net"
+ "net/netip"
+
+ "github.com/metacubex/mihomo/component/slowdown"
+ C "github.com/metacubex/mihomo/constant"
+)
+
+type SlowDownDialer struct {
+ C.Dialer
+ Slowdown *slowdown.SlowDown
+}
+
+func (d SlowDownDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
+ return slowdown.Do(d.Slowdown, ctx, func() (net.Conn, error) {
+ return d.Dialer.DialContext(ctx, network, address)
+ })
+}
+
+func (d SlowDownDialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) {
+ return slowdown.Do(d.Slowdown, ctx, func() (net.PacketConn, error) {
+ return d.Dialer.ListenPacket(ctx, network, address, rAddrPort)
+ })
+}
+
+func NewSlowDownDialer(d C.Dialer, sd *slowdown.SlowDown) SlowDownDialer {
+ return SlowDownDialer{
+ Dialer: d,
+ Slowdown: sd,
+ }
+}
diff --git a/component/proxydialer/slowdown_sing.go b/component/proxydialer/slowdown_sing.go
new file mode 100644
index 00000000..cc3a46aa
--- /dev/null
+++ b/component/proxydialer/slowdown_sing.go
@@ -0,0 +1,33 @@
+package proxydialer
+
+import (
+ "context"
+ "net"
+
+ "github.com/metacubex/mihomo/component/slowdown"
+ M "github.com/sagernet/sing/common/metadata"
+)
+
+type SlowDownSingDialer struct {
+ SingDialer
+ Slowdown *slowdown.SlowDown
+}
+
+func (d SlowDownSingDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
+ return slowdown.Do(d.Slowdown, ctx, func() (net.Conn, error) {
+ return d.SingDialer.DialContext(ctx, network, destination)
+ })
+}
+
+func (d SlowDownSingDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
+ return slowdown.Do(d.Slowdown, ctx, func() (net.PacketConn, error) {
+ return d.SingDialer.ListenPacket(ctx, destination)
+ })
+}
+
+func NewSlowDownSingDialer(d SingDialer, sd *slowdown.SlowDown) SlowDownSingDialer {
+ return SlowDownSingDialer{
+ SingDialer: d,
+ Slowdown: sd,
+ }
+}
diff --git a/component/slowdown/backoff.go b/component/slowdown/backoff.go
new file mode 100644
index 00000000..a5a7251c
--- /dev/null
+++ b/component/slowdown/backoff.go
@@ -0,0 +1,101 @@
+// modify from https://github.com/jpillora/backoff/blob/v1.0.0/backoff.go
+
+package slowdown
+
+import (
+ "math"
+ "math/rand"
+ "sync/atomic"
+ "time"
+)
+
+// Backoff is a time.Duration counter, starting at Min. After every call to
+// the Duration method the current timing is multiplied by Factor, but it
+// never exceeds Max.
+//
+// Backoff is not generally concurrent-safe, but the ForAttempt method can
+// be used concurrently.
+type Backoff struct {
+ attempt atomic.Uint64
+ // Factor is the multiplying factor for each increment step
+ Factor float64
+ // Jitter eases contention by randomizing backoff steps
+ Jitter bool
+ // Min and Max are the minimum and maximum values of the counter
+ Min, Max time.Duration
+}
+
+// Duration returns the duration for the current attempt before incrementing
+// the attempt counter. See ForAttempt.
+func (b *Backoff) Duration() time.Duration {
+ d := b.ForAttempt(float64(b.attempt.Add(1) - 1))
+ return d
+}
+
+const maxInt64 = float64(math.MaxInt64 - 512)
+
+// ForAttempt returns the duration for a specific attempt. This is useful if
+// you have a large number of independent Backoffs, but don't want use
+// unnecessary memory storing the Backoff parameters per Backoff. The first
+// attempt should be 0.
+//
+// ForAttempt is concurrent-safe.
+func (b *Backoff) ForAttempt(attempt float64) time.Duration {
+ // Zero-values are nonsensical, so we use
+ // them to apply defaults
+ min := b.Min
+ if min <= 0 {
+ min = 100 * time.Millisecond
+ }
+ max := b.Max
+ if max <= 0 {
+ max = 10 * time.Second
+ }
+ if min >= max {
+ // short-circuit
+ return max
+ }
+ factor := b.Factor
+ if factor <= 0 {
+ factor = 2
+ }
+ //calculate this duration
+ minf := float64(min)
+ durf := minf * math.Pow(factor, attempt)
+ if b.Jitter {
+ durf = rand.Float64()*(durf-minf) + minf
+ }
+ //ensure float64 wont overflow int64
+ if durf > maxInt64 {
+ return max
+ }
+ dur := time.Duration(durf)
+ //keep within bounds
+ if dur < min {
+ return min
+ }
+ if dur > max {
+ return max
+ }
+ return dur
+}
+
+// Reset restarts the current attempt counter at zero.
+func (b *Backoff) Reset() {
+ b.attempt.Store(0)
+}
+
+// Attempt returns the current attempt counter value.
+func (b *Backoff) Attempt() float64 {
+ return float64(b.attempt.Load())
+}
+
+// Copy returns a backoff with equals constraints as the original
+func (b *Backoff) Copy() *Backoff {
+ return &Backoff{
+ Factor: b.Factor,
+ Jitter: b.Jitter,
+ Min: b.Min,
+ Max: b.Max,
+ }
+}
diff --git a/component/slowdown/slowdown.go b/component/slowdown/slowdown.go
new file mode 100644
index 00000000..3fc12191
--- /dev/null
+++ b/component/slowdown/slowdown.go
@@ -0,0 +1,49 @@
+package slowdown
+
+import (
+ "context"
+ "sync/atomic"
+ "time"
+)
+
+type SlowDown struct {
+ errTimes atomic.Int64
+ backoff Backoff
+}
+
+func (s *SlowDown) Wait(ctx context.Context) (err error) {
+ select {
+ case <-time.After(s.backoff.Duration()):
+ case <-ctx.Done():
+ err = ctx.Err()
+ }
+ return
+}
+
+func New() *SlowDown {
+ return &SlowDown{
+ backoff: Backoff{
+ Min: 10 * time.Millisecond,
+ Max: 1 * time.Second,
+ Factor: 2,
+ Jitter: true,
+ },
+ }
+}
+
+func Do[T any](s *SlowDown, ctx context.Context, fn func() (T, error)) (t T, err error) {
+ if s.errTimes.Load() > 10 {
+ err = s.Wait(ctx)
+ if err != nil {
+ return
+ }
+ }
+ t, err = fn()
+ if err != nil {
+ s.errTimes.Add(1)
+ return
+ }
+ s.errTimes.Store(0)
+ s.backoff.Reset()
+ return
+}
diff --git a/go.mod b/go.mod
index c2571ca2..54a0a5dc 100644
--- a/go.mod
+++ b/go.mod
@@ -15,7 +15,6 @@ require (
github.com/gobwas/ws v1.3.2
github.com/gofrs/uuid/v5 v5.0.0
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2
- github.com/jpillora/backoff v1.0.0
github.com/klauspost/cpuid/v2 v2.2.6
github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40
github.com/mdlayher/netlink v1.7.2
diff --git a/go.sum b/go.sum
index 8b821f53..a7f6cd02 100644
--- a/go.sum
+++ b/go.sum
@@ -84,8 +84,6 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF
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/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go
index cba36d9c..f8fdcf11 100644
--- a/tunnel/tunnel.go
+++ b/tunnel/tunnel.go
@@ -11,12 +11,11 @@ import (
"sync"
"time"
- "github.com/jpillora/backoff"
-
N "github.com/metacubex/mihomo/common/net"
"github.com/metacubex/mihomo/component/nat"
P "github.com/metacubex/mihomo/component/process"
"github.com/metacubex/mihomo/component/resolver"
+ "github.com/metacubex/mihomo/component/slowdown"
"github.com/metacubex/mihomo/component/sniffer"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/constant/features"
@@ -699,12 +698,7 @@ func shouldStopRetry(err error) bool {
}
func retry[T any](ctx context.Context, ft func(context.Context) (T, error), fe func(err error)) (t T, err error) {
- b := &backoff.Backoff{
- Min: 10 * time.Millisecond,
- Max: 1 * time.Second,
- Factor: 2,
- Jitter: true,
- }
+ s := slowdown.New()
for i := 0; i < 10; i++ {
t, err = ft(ctx)
if err != nil {
@@ -714,10 +708,9 @@ func retry[T any](ctx context.Context, ft func(context.Context) (T, error), fe f
if shouldStopRetry(err) {
return
}
- select {
- case <-time.After(b.Duration()):
+ if s.Wait(ctx) == nil {
continue
- case <-ctx.Done():
+ } else {
return
}
} else {