From 822ba5f0b56fc2acb4e5afc94df7a6ea10c85c7a Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Mon, 5 Feb 2024 13:56:39 +0800 Subject: [PATCH 01/65] ci: bump github-actions version --- .github/workflows/build.yml | 47 +++++++++++++++++++------------------ Makefile | 1 + 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6bd6dad0..173aeb78 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -66,15 +66,21 @@ jobs: } - { type: "WithoutCGO", - target: "darwin-amd64 darwin-arm64 android-arm64", + target: "darwin-amd64 darwin-arm64", id: "9", } + - { + type: "WithoutCGO", + target: "darwin-amd64-compatible android-arm64", + id: "10", + } # only for test - { type: "WithoutCGO-GO120", target: "linux-amd64 linux-amd64-compatible",id: "1" } # Go 1.20 is the last release that will run on any release of Windows 7, 8, Server 2008 and Server 2012. Go 1.21 will require at least Windows 10 or Server 2016. - { type: "WithoutCGO-GO120", target: "windows-amd64-compatible windows-amd64 windows-386",id: "2" } # Go 1.20 is the last release that will run on macOS 10.13 High Sierra or 10.14 Mojave. Go 1.21 will require macOS 10.15 Catalina or later. - - { type: "WithoutCGO-GO120", target: "darwin-amd64 darwin-arm64 android-arm64",id: "3" } + - { type: "WithoutCGO-GO120", target: "darwin-amd64 darwin-arm64",id: "3" } + - { type: "WithoutCGO-GO120", target: "darwin-amd64-compatible android-arm64",id: "4" } # - { type: "WithCGO", target: "windows/*", id: "1" } # - { type: "WithCGO", target: "linux/386", id: "2" } # - { type: "WithCGO", target: "linux/amd64", id: "3" } @@ -89,7 +95,7 @@ jobs: steps: - name: Check out code into the Go module directory - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set variables run: echo "VERSION=$(git rev-parse --short HEAD)" >> $GITHUB_ENV @@ -100,11 +106,6 @@ jobs: run: echo "VERSION=alpha-$(git rev-parse --short HEAD)" >> $GITHUB_ENV shell: bash - - name: Set variables - if: ${{github.ref_name=='Beta'}} - run: echo "VERSION=beta-$(git rev-parse --short HEAD)" >> $GITHUB_ENV - shell: bash - - name: Set variables if: ${{github.ref_name=='Meta'}} run: echo "VERSION=meta-$(git rev-parse --short HEAD)" >> $GITHUB_ENV @@ -123,21 +124,21 @@ jobs: - name: Set ENV run: | - echo "TAGS=with_gvisor,with_lwip" >> $GITHUB_ENV + echo "TAGS=with_gvisor" >> $GITHUB_ENV echo "LDFLAGS=-X 'github.com/metacubex/mihomo/constant.Version=${VERSION}' -X 'github.com/metacubex/mihomo/constant.BuildTime=${BUILDTIME}' -w -s -buildid=" >> $GITHUB_ENV echo "GOTOOLCHAIN=local" >> $GITHUB_ENV shell: bash - name: Setup Go if: ${{ matrix.job.type!='WithoutCGO-GO120' }} - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: "1.21" check-latest: true - name: Setup Go if: ${{ matrix.job.type=='WithoutCGO-GO120' }} - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: "1.20" check-latest: true @@ -222,10 +223,10 @@ jobs: run: echo ${VERSION} > bin/version.txt shell: bash - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: ${{ success() }} with: - name: artifact + name: artifact-${{ matrix.job.type }}-${{ matrix.job.target }} path: bin/ Upload-Prerelease: @@ -234,10 +235,10 @@ jobs: needs: [Build] runs-on: ubuntu-latest steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: - name: artifact path: bin/ + merge-multiple: true - name: Display structure of downloaded files run: ls -R @@ -261,14 +262,14 @@ jobs: tag_name: Prerelease-${{ github.ref_name }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - + - run: | cat > release.txt << 'EOF' Release created at ${{ env.BUILDTIME }} 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) + [二进制文件筛选 / Binary file selector](https://metacubex.github.io/Meta-Docs/startup/#_1) [查看文档 / Docs](https://metacubex.github.io/Meta-Docs/) EOF @@ -290,7 +291,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -306,10 +307,10 @@ jobs: bash ./genReleaseNote.sh -v ${PREVERSION}...${CURRENTVERSION} rm ./genReleaseNote.sh - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: - name: artifact path: bin/ + merge-multiple: true - name: Display structure of downloaded files run: ls -R @@ -331,14 +332,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: - name: artifact path: bin/ + merge-multiple: true - name: Display structure of downloaded files run: ls -R diff --git a/Makefile b/Makefile index f6ffcae5..59bec41e 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,7 @@ GOBUILD=CGO_ENABLED=0 go build -tags with_gvisor -trimpath -ldflags '-X "github. -w -s -buildid=' PLATFORM_LIST = \ + darwin-amd64-compatible \ darwin-amd64 \ darwin-arm64 \ linux-amd64-compatible \ From 20658f6eac2b9fb876b377547ee0190eb2e57b05 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Mon, 5 Feb 2024 22:40:06 +0800 Subject: [PATCH 02/65] fix: `lan-allowed-ips` does not take effect --- listener/http/server.go | 4 ++-- listener/mixed/mixed.go | 4 ++-- listener/socks/tcp.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/listener/http/server.go b/listener/http/server.go index 3ff1679d..4ed59709 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -71,8 +71,8 @@ func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additi t.SetKeepAlive(false) } } - if len(additions) == 0 { // only apply on default listener - if inbound.IsRemoteAddrDisAllowed(conn.RemoteAddr()) { + if len(additions) == 2 { // only apply on default listener + if !inbound.IsRemoteAddrDisAllowed(conn.RemoteAddr()) { _ = conn.Close() continue } diff --git a/listener/mixed/mixed.go b/listener/mixed/mixed.go index 94613039..b645cbbb 100644 --- a/listener/mixed/mixed.go +++ b/listener/mixed/mixed.go @@ -62,8 +62,8 @@ func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener } continue } - if len(additions) == 0 { // only apply on default listener - if inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) { + if len(additions) == 2 { // only apply on default listener + if !inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) { _ = c.Close() continue } diff --git a/listener/socks/tcp.go b/listener/socks/tcp.go index 8016e958..a33f190e 100644 --- a/listener/socks/tcp.go +++ b/listener/socks/tcp.go @@ -59,8 +59,8 @@ func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener } continue } - if len(additions) == 0 { // only apply on default listener - if inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) { + if len(additions) == 2 { // only apply on default listener + if !inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) { _ = c.Close() continue } From 9e57e7d29b3d6d25e6ad116e83d81ef059c7aedb Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 7 Feb 2024 18:22:54 +0800 Subject: [PATCH 03/65] fix: fix `lan-allowed-ips` does not take effect --- listener/http/server.go | 4 +++- listener/mixed/mixed.go | 4 +++- listener/socks/tcp.go | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/listener/http/server.go b/listener/http/server.go index 4ed59709..8fc9da59 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -36,7 +36,9 @@ func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener } func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additions ...inbound.Addition) (*Listener, error) { + isDefault := false if len(additions) == 0 { + isDefault = true additions = []inbound.Addition{ inbound.WithInName("DEFAULT-HTTP"), inbound.WithSpecialRules(""), @@ -71,7 +73,7 @@ func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additi t.SetKeepAlive(false) } } - if len(additions) == 2 { // only apply on default listener + if isDefault { // only apply on default listener if !inbound.IsRemoteAddrDisAllowed(conn.RemoteAddr()) { _ = conn.Close() continue diff --git a/listener/mixed/mixed.go b/listener/mixed/mixed.go index b645cbbb..367b7a36 100644 --- a/listener/mixed/mixed.go +++ b/listener/mixed/mixed.go @@ -37,7 +37,9 @@ func (l *Listener) Close() error { } func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) { + isDefault := false if len(additions) == 0 { + isDefault = true additions = []inbound.Addition{ inbound.WithInName("DEFAULT-MIXED"), inbound.WithSpecialRules(""), @@ -62,7 +64,7 @@ func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener } continue } - if len(additions) == 2 { // only apply on default listener + if isDefault { // only apply on default listener if !inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) { _ = c.Close() continue diff --git a/listener/socks/tcp.go b/listener/socks/tcp.go index a33f190e..b6ea023a 100644 --- a/listener/socks/tcp.go +++ b/listener/socks/tcp.go @@ -35,7 +35,9 @@ func (l *Listener) Close() error { } func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) { + isDefault := false if len(additions) == 0 { + isDefault = true additions = []inbound.Addition{ inbound.WithInName("DEFAULT-SOCKS"), inbound.WithSpecialRules(""), @@ -59,7 +61,7 @@ func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener } continue } - if len(additions) == 2 { // only apply on default listener + if isDefault { // only apply on default listener if !inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) { _ = c.Close() continue From 324c0bde7dfbe1aa5f6205395b2c3cfd9ff78e4d Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 7 Feb 2024 18:23:18 +0800 Subject: [PATCH 04/65] chore: update golang to 1.22 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 173aeb78..922de5d7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -133,7 +133,7 @@ jobs: if: ${{ matrix.job.type!='WithoutCGO-GO120' }} uses: actions/setup-go@v5 with: - go-version: "1.21" + go-version: "1.22" check-latest: true - name: Setup Go From 0c384b1e4225709d1dcec61db37dc8af0388bfa3 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 7 Feb 2024 21:07:41 +0800 Subject: [PATCH 05/65] fix: tproxy start error --- listener/tproxy/setsockopt_linux.go | 18 +++++++++++++----- listener/tproxy/udp_linux.go | 20 +++++++++++++++++--- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/listener/tproxy/setsockopt_linux.go b/listener/tproxy/setsockopt_linux.go index b83b28a4..9189f115 100644 --- a/listener/tproxy/setsockopt_linux.go +++ b/listener/tproxy/setsockopt_linux.go @@ -36,13 +36,21 @@ func setsockopt(rc syscall.RawConn, addr string) error { } 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) + _ = setDSCPsockopt(fd, isIPv6) } }) return err } + +func setDSCPsockopt(fd uintptr, isIPv6 bool) (err error) { + if err == nil { + err = syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_RECVTOS, 1) + } + + if err == nil && isIPv6 { + err = syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, syscall.IPV6_RECVTCLASS, 1) + } + + return +} diff --git a/listener/tproxy/udp_linux.go b/listener/tproxy/udp_linux.go index 02b51379..c96d4cc7 100644 --- a/listener/tproxy/udp_linux.go +++ b/listener/tproxy/udp_linux.go @@ -104,7 +104,14 @@ func getOrigDst(oob []byte) (netip.AddrPort, error) { } // retrieve the destination address from the SCM. - sa, err := unix.ParseOrigDstAddr(&scms[1]) + var sa unix.Sockaddr + for i := range scms { + sa, err = unix.ParseOrigDstAddr(&scms[i]) + if err == nil { + break + } + } + if err != nil { return netip.AddrPort{}, fmt.Errorf("retrieve destination: %w", err) } @@ -123,12 +130,19 @@ func getOrigDst(oob []byte) (netip.AddrPort, error) { return rAddr, nil } -func getDSCP (oob []byte) (uint8, error) { +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]) + var dscp uint8 + for i := range scms { + dscp, err = parseDSCP(&scms[i]) + if err == nil { + break + } + } + if err != nil { return 0, fmt.Errorf("retrieve DSCP: %w", err) } From 080d316059d9bceff569370f36c19f3de570ec5b Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 14 Feb 2024 18:07:40 +0800 Subject: [PATCH 06/65] chore: update gvisor --- go.mod | 12 ++++++------ go.sum | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index 54a0a5dc..bba88573 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,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.1-0.20240130042529-1f983547e9d4 + github.com/metacubex/sing-tun v0.2.1-0.20240214100323-23e40bfb9067 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 @@ -47,11 +47,11 @@ require ( 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.18.0 + golang.org/x/crypto v0.19.0 golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e - golang.org/x/net v0.20.0 + golang.org/x/net v0.21.0 golang.org/x/sync v0.6.0 - golang.org/x/sys v0.16.0 + golang.org/x/sys v0.17.0 google.golang.org/protobuf v1.32.0 gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.2.1 @@ -84,7 +84,7 @@ require ( github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mdlayher/socket v0.4.1 // indirect - github.com/metacubex/gvisor v0.0.0-20231209122014-3e43224c7bbc // indirect + github.com/metacubex/gvisor v0.0.0-20240214095142-666a73bcf165 // indirect github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/pierrec/lz4/v4 v4.1.14 // indirect @@ -108,7 +108,7 @@ require ( golang.org/x/mod v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.16.0 // indirect + golang.org/x/tools v0.16.1 // indirect ) replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240111014253-f1818b6a82b2 diff --git a/go.sum b/go.sum index a7f6cd02..4e41bf44 100644 --- a/go.sum +++ b/go.sum @@ -102,8 +102,8 @@ github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI= 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/gvisor v0.0.0-20240214095142-666a73bcf165 h1:QIQI4gEm+gTwVNdiAyF4EIz5cHm7kSlfDGFpYlAa5dg= +github.com/metacubex/gvisor v0.0.0-20240214095142-666a73bcf165/go.mod h1:SKY70wiF1UTSoyuDZyKPMsUC6MsMxh8Y3ZNkIa6J3fU= 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= @@ -114,8 +114,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.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-tun v0.2.1-0.20240214100323-23e40bfb9067 h1:sB9Hiq/Fgq94WD1mAFDUTDaQAJ6y3WZ5nZMEavcK0/o= +github.com/metacubex/sing-tun v0.2.1-0.20240214100323-23e40bfb9067/go.mod h1:HWyO52kAVvuSUN2nms4ZlRfiAwgXO9wGQBJFjymqvOQ= 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= @@ -222,8 +222,8 @@ 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.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= 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= @@ -232,8 +232,8 @@ 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.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -253,16 +253,16 @@ 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/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/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.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= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -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/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= +golang.org/x/tools v0.16.1/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.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= From 93b48a94fc4f015e485f2a610fcb4e8a5ca2742f Mon Sep 17 00:00:00 2001 From: H1JK Date: Thu, 15 Feb 2024 21:48:48 +0800 Subject: [PATCH 07/65] chore: Update workflow --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 922de5d7..654e576f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -257,7 +257,7 @@ jobs: shell: bash - name: Tag Repo - uses: richardsimko/update-tag@v1.0.6 + uses: richardsimko/update-tag@v1 with: tag_name: Prerelease-${{ github.ref_name }} env: @@ -346,10 +346,10 @@ jobs: working-directory: bin - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - name: Setup Docker buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 with: version: latest @@ -357,7 +357,7 @@ jobs: # https://github.com/docker/metadata-action - name: Extract Docker metadata id: meta - uses: docker/metadata-action@v4 + uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY }}/${{ github.repository }} From 985b884d85490226f3374c4d2aa831e33ae20739 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 16 Feb 2024 11:25:10 +0800 Subject: [PATCH 08/65] chore: add power event code for windows --- component/power/event.go | 22 ++++++++++ component/power/event_other.go | 9 ++++ component/power/event_windows.go | 74 ++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 component/power/event.go create mode 100644 component/power/event_other.go create mode 100644 component/power/event_windows.go diff --git a/component/power/event.go b/component/power/event.go new file mode 100644 index 00000000..f59c2ad4 --- /dev/null +++ b/component/power/event.go @@ -0,0 +1,22 @@ +package power + +type Type uint8 + +const ( + SUSPEND Type = iota + RESUME + RESUMEAUTOMATIC // Because the user is not present, most applications should do nothing. +) + +func (t Type) String() string { + switch t { + case SUSPEND: + return "SUSPEND" + case RESUME: + return "RESUME" + case RESUMEAUTOMATIC: + return "RESUMEAUTOMATIC" + default: + return "" + } +} diff --git a/component/power/event_other.go b/component/power/event_other.go new file mode 100644 index 00000000..3a41d9e0 --- /dev/null +++ b/component/power/event_other.go @@ -0,0 +1,9 @@ +//go:build !windows + +package power + +import "errors" + +func NewEventListener(cb func(Type)) (func(), error) { + return nil, errors.New("not support on this platform") +} diff --git a/component/power/event_windows.go b/component/power/event_windows.go new file mode 100644 index 00000000..12655695 --- /dev/null +++ b/component/power/event_windows.go @@ -0,0 +1,74 @@ +package power + +// modify from https://github.com/golang/go/blob/b634f6fdcbebee23b7da709a243f3db217b64776/src/runtime/os_windows.go#L257 + +import ( + "runtime" + "unsafe" + + "golang.org/x/sys/windows" +) + +var ( + libPowrProf = windows.NewLazySystemDLL("powrprof.dll") + powerRegisterSuspendResumeNotification = libPowrProf.NewProc("PowerRegisterSuspendResumeNotification") + powerUnregisterSuspendResumeNotification = libPowrProf.NewProc("PowerUnregisterSuspendResumeNotification") +) + +func NewEventListener(cb func(Type)) (func(), error) { + if err := powerRegisterSuspendResumeNotification.Find(); err != nil { + return nil, err // Running on Windows 7, where we don't need it anyway. + } + if err := powerUnregisterSuspendResumeNotification.Find(); err != nil { + return nil, err // Running on Windows 7, where we don't need it anyway. + } + + // Defines the type of event + const ( + PBT_APMSUSPEND uint32 = 4 + PBT_APMRESUMESUSPEND uint32 = 7 + PBT_APMRESUMEAUTOMATIC uint32 = 18 + ) + + const ( + _DEVICE_NOTIFY_CALLBACK = 2 + ) + type _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS struct { + callback uintptr + context uintptr + } + + var fn interface{} = func(context uintptr, changeType uint32, setting uintptr) uintptr { + switch changeType { + case PBT_APMSUSPEND: + cb(SUSPEND) + case PBT_APMRESUMESUSPEND: + cb(RESUME) + case PBT_APMRESUMEAUTOMATIC: + cb(RESUMEAUTOMATIC) + } + return 0 + } + + params := _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS{ + callback: windows.NewCallback(fn), + } + handle := uintptr(0) + + _, _, err := powerRegisterSuspendResumeNotification.Call( + _DEVICE_NOTIFY_CALLBACK, + uintptr(unsafe.Pointer(¶ms)), + uintptr(unsafe.Pointer(&handle)), + ) + if err != nil { + return nil, err + } + + return func() { + _, _, _ = powerUnregisterSuspendResumeNotification.Call( + uintptr(unsafe.Pointer(&handle)), + ) + runtime.KeepAlive(params) + runtime.KeepAlive(handle) + }, nil +} From 23e3f12e881c4626af7d06336bac77e717b00bea Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 16 Feb 2024 11:29:33 +0800 Subject: [PATCH 09/65] chore: better timer using --- component/slowdown/slowdown.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/component/slowdown/slowdown.go b/component/slowdown/slowdown.go index 3fc12191..eff4915f 100644 --- a/component/slowdown/slowdown.go +++ b/component/slowdown/slowdown.go @@ -12,8 +12,10 @@ type SlowDown struct { } func (s *SlowDown) Wait(ctx context.Context) (err error) { + timer := time.NewTimer(s.backoff.Duration()) + defer timer.Stop() select { - case <-time.After(s.backoff.Duration()): + case <-timer.C: case <-ctx.Done(): err = ctx.Err() } From 6399347a63bcc7262bca9b0f14554430ee1818fe Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Tue, 20 Feb 2024 15:15:22 +0800 Subject: [PATCH 10/65] chore: add some fields for override --- adapter/provider/parser.go | 18 ++++++++++-------- adapter/provider/provider.go | 8 ++++++++ docs/config.yaml | 2 ++ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/adapter/provider/parser.go b/adapter/provider/parser.go index 88b36b7c..2e436669 100644 --- a/adapter/provider/parser.go +++ b/adapter/provider/parser.go @@ -28,14 +28,16 @@ type healthCheckSchema struct { } type OverrideSchema struct { - UDP *bool `provider:"udp,omitempty"` - Up *string `provider:"up,omitempty"` - Down *string `provider:"down,omitempty"` - DialerProxy *string `provider:"dialer-proxy,omitempty"` - SkipCertVerify *bool `provider:"skip-cert-verify,omitempty"` - Interface *string `provider:"interface-name,omitempty"` - RoutingMark *int `provider:"routing-mark,omitempty"` - IPVersion *string `provider:"ip-version,omitempty"` + UDP *bool `provider:"udp,omitempty"` + Up *string `provider:"up,omitempty"` + Down *string `provider:"down,omitempty"` + DialerProxy *string `provider:"dialer-proxy,omitempty"` + SkipCertVerify *bool `provider:"skip-cert-verify,omitempty"` + Interface *string `provider:"interface-name,omitempty"` + RoutingMark *int `provider:"routing-mark,omitempty"` + IPVersion *string `provider:"ip-version,omitempty"` + AdditionalPrefix *string `provider:"additional-prefix,omitempty"` + AdditionalSuffix *string `provider:"additional-suffix,omitempty"` } type proxyProviderSchema struct { diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index d710d3a4..b591538b 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -399,6 +399,14 @@ func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray if override.IPVersion != nil { mapping["ip-version"] = *override.IPVersion } + if override.AdditionalPrefix != nil { + name := mapping["name"].(string) + mapping["name"] = *override.AdditionalPrefix + name + } + if override.AdditionalSuffix != nil { + name := mapping["name"].(string) + mapping["name"] = name + *override.AdditionalSuffix + } proxy, err := adapter.ParseProxy(mapping) if err != nil { diff --git a/docs/config.yaml b/docs/config.yaml index eee6122b..dc257ee2 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -862,6 +862,8 @@ proxy-providers: # interface-name: tailscale0 # routing-mark: 233 # ip-version: ipv4-prefer + # additional-prefix: "[provider1]" + # additional-suffix: "test" test: type: file path: /test.yaml From 9e7eaf720f1b5d4e27c3a4eec868165a384b38ce Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 21 Feb 2024 15:04:25 +0800 Subject: [PATCH 11/65] fix: ipv6 http host addr --- listener/http/utils.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/listener/http/utils.go b/listener/http/utils.go index 63726d51..e67c0fde 100644 --- a/listener/http/utils.go +++ b/listener/http/utils.go @@ -5,6 +5,7 @@ import ( "errors" "net" "net/http" + "net/netip" "strings" ) @@ -48,6 +49,11 @@ func removeExtraHTTPHostPort(req *http.Request) { if pHost, port, err := net.SplitHostPort(host); err == nil && (port == "80" || port == "443") { host = pHost + if ip, err := netip.ParseAddr(pHost); err == nil && ip.Is6() { + // RFC 2617 Sec 3.2.2, for IPv6 literal + // addresses the Host header needs to follow the RFC 2732 grammar for "host" + host = "[" + host + "]" + } } req.Host = host From 1c7e011f8708ef0804a331bd6f9fdfbb7390653b Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Wed, 21 Feb 2024 17:14:08 +0800 Subject: [PATCH 12/65] fix: api does not return configuration value --- component/geodata/utils.go | 34 +++++++++++++++++++++++++++++++--- config/config.go | 3 +++ hub/executor/executor.go | 21 ++++++++++++--------- 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/component/geodata/utils.go b/component/geodata/utils.go index a4002aeb..981d7eba 100644 --- a/component/geodata/utils.go +++ b/component/geodata/utils.go @@ -3,19 +3,37 @@ package geodata import ( "errors" "fmt" - "golang.org/x/sync/singleflight" "strings" + "golang.org/x/sync/singleflight" + "github.com/metacubex/mihomo/component/geodata/router" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" ) -var geoLoaderName = "memconservative" -var geoSiteMatcher = "succinct" +var ( + geoMode bool + AutoUpdate bool + UpdateInterval int + geoLoaderName = "memconservative" + geoSiteMatcher = "succinct" +) // geoLoaderName = "standard" +func GeodataMode() bool { + return geoMode +} + +func GeoAutoUpdate() bool { + return AutoUpdate +} + +func GeoUpdateInterval() int { + return UpdateInterval +} + func LoaderName() string { return geoLoaderName } @@ -24,6 +42,16 @@ func SiteMatcherName() string { return geoSiteMatcher } +func SetGeodataMode(newGeodataMode bool) { + geoMode = newGeodataMode +} +func SetGeoAutoUpdate(newAutoUpdate bool) { + AutoUpdate = newAutoUpdate +} +func SetGeoUpdateInterval(newGeoUpdateInterval int) { + UpdateInterval = newGeoUpdateInterval +} + func SetLoader(newLoader string) { if newLoader == "memc" { newLoader = "memconservative" diff --git a/config/config.go b/config/config.go index b0401596..c5f4bb77 100644 --- a/config/config.go +++ b/config/config.go @@ -607,6 +607,9 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } func parseGeneral(cfg *RawConfig) (*General, error) { + geodata.SetGeodataMode(cfg.GeodataMode) + geodata.SetGeoAutoUpdate(cfg.GeoAutoUpdate) + geodata.SetGeoUpdateInterval(cfg.GeoUpdateInterval) geodata.SetLoader(cfg.GeodataLoader) geodata.SetSiteMatcher(cfg.GeositeMatcher) C.GeoAutoUpdate = cfg.GeoAutoUpdate diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 783da4d3..e4a31a79 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -146,15 +146,18 @@ func GetGeneral() *config.General { AllowLan: listener.AllowLan(), BindAddress: listener.BindAddress(), }, - Controller: config.Controller{}, - Mode: tunnel.Mode(), - LogLevel: log.Level(), - IPv6: !resolver.DisableIPv6, - GeodataLoader: G.LoaderName(), - GeositeMatcher: G.SiteMatcherName(), - Interface: dialer.DefaultInterface.Load(), - Sniffing: tunnel.IsSniffing(), - TCPConcurrent: dialer.GetTcpConcurrent(), + Controller: config.Controller{}, + Mode: tunnel.Mode(), + LogLevel: log.Level(), + IPv6: !resolver.DisableIPv6, + GeodataMode: G.GeodataMode(), + GeoAutoUpdate: G.GeoAutoUpdate(), + GeoUpdateInterval: G.GeoUpdateInterval(), + GeodataLoader: G.LoaderName(), + GeositeMatcher: G.SiteMatcherName(), + Interface: dialer.DefaultInterface.Load(), + Sniffing: tunnel.IsSniffing(), + TCPConcurrent: dialer.GetTcpConcurrent(), } return general From 3d833ef6a8332f8409d546bbb1ce7524c3a535f9 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Wed, 21 Feb 2024 21:00:33 +0800 Subject: [PATCH 13/65] chore: don't panic when set deadline error --- dns/doh.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dns/doh.go b/dns/doh.go index 9e173c84..ef4b653f 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -709,7 +709,8 @@ func (doh *dnsOverHTTPS) tlsDial(ctx context.Context, dialContext dialHandler, n err = conn.SetDeadline(time.Now().Add(dialTimeout)) if err != nil { // Must not happen in normal circumstances. - panic(fmt.Errorf("cannot set deadline: %w", err)) + log.Errorln("cannot set deadline: %v", err) + return nil, err } err = conn.Handshake() From f8295a02fde11dd14b81ca3c84184cb66d86c177 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Wed, 21 Feb 2024 21:56:20 +0800 Subject: [PATCH 14/65] fix: update mmdb fail --- component/mmdb/mmdb.go | 6 +++++- config/update_geo.go | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/component/mmdb/mmdb.go b/component/mmdb/mmdb.go index d411b2b4..24edb5e5 100644 --- a/component/mmdb/mmdb.go +++ b/component/mmdb/mmdb.go @@ -57,7 +57,7 @@ func Verify() bool { func Instance() Reader { once.Do(func() { mmdbPath := C.Path.MMDB() - log.Debugln("Load MMDB file: %s", mmdbPath) + log.Infoln("Load MMDB file: %s", mmdbPath) mmdb, err := maxminddb.Open(mmdbPath) if err != nil { log.Fatalln("Can't load MMDB: %s", err.Error()) @@ -94,3 +94,7 @@ func DownloadMMDB(path string) (err error) { return err } + +func Reload() { + once = sync.Once{} +} diff --git a/config/update_geo.go b/config/update_geo.go index 2cde47a1..bf3d0810 100644 --- a/config/update_geo.go +++ b/config/update_geo.go @@ -6,6 +6,7 @@ import ( "github.com/metacubex/mihomo/component/geodata" _ "github.com/metacubex/mihomo/component/geodata/standard" + "github.com/metacubex/mihomo/component/mmdb" C "github.com/metacubex/mihomo/constant" "github.com/oschwald/maxminddb-golang" @@ -33,6 +34,7 @@ func UpdateGeoDatabases() error { } } else { + defer mmdb.Reload() data, err := downloadForBytes(C.MmdbUrl) if err != nil { return fmt.Errorf("can't download MMDB database file: %w", err) @@ -43,6 +45,8 @@ func UpdateGeoDatabases() error { return fmt.Errorf("invalid MMDB database file: %s", err) } _ = instance.Close() + + mmdb.Instance().Reader.Close() // mmdb is loaded with mmap, so it needs to be closed before overwriting the file if err = saveFile(data, C.Path.MMDB()); err != nil { return fmt.Errorf("can't save MMDB database file: %w", err) } From d2a5376cb8ad47ebc7461710ed09e2908c4ae587 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Wed, 21 Feb 2024 22:49:28 +0800 Subject: [PATCH 15/65] revert: "modify default url" This reverts commit 3d643cb95a32e0cef69c4411af9655fa0ee6851a. --- adapter/outboundgroup/parser.go | 39 ++++++++++++++------------------- adapter/provider/healthcheck.go | 4 ++-- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/adapter/outboundgroup/parser.go b/adapter/outboundgroup/parser.go index 959041dd..2fa8d4ec 100644 --- a/adapter/outboundgroup/parser.go +++ b/adapter/outboundgroup/parser.go @@ -64,20 +64,15 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide groupOption.IncludeAllProviders = true groupOption.IncludeAllProxies = true } - var GroupUse []string - var GroupProxies []string + if groupOption.IncludeAllProviders { - GroupUse = append(GroupUse, AllProviders...) - } else { - GroupUse = groupOption.Use + groupOption.Use = append(groupOption.Use, AllProviders...) } if groupOption.IncludeAllProxies { - GroupProxies = append(groupOption.Proxies, AllProxies...) - } else { - GroupProxies = groupOption.Proxies + groupOption.Proxies = append(groupOption.Proxies, AllProxies...) } - if len(GroupProxies) == 0 && len(GroupUse) == 0 { + if len(groupOption.Proxies) == 0 && len(groupOption.Use) == 0 { return nil, fmt.Errorf("%s: %w", groupName, errMissProxy) } @@ -91,17 +86,9 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide status = "*" } groupOption.ExpectedStatus = status - testUrl := groupOption.URL - if groupOption.Type != "select" && groupOption.Type != "relay" { - if groupOption.URL == "" { - groupOption.URL = C.DefaultTestURL - testUrl = groupOption.URL - } - } - - if len(GroupProxies) != 0 { - ps, err := getProxies(proxyMap, GroupProxies) + if len(groupOption.Proxies) != 0 { + ps, err := getProxies(proxyMap, groupOption.Proxies) if err != nil { return nil, fmt.Errorf("%s: %w", groupName, err) } @@ -115,9 +102,12 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide if groupOption.Interval == 0 { groupOption.Interval = 300 } + if groupOption.URL == "" { + groupOption.URL = C.DefaultTestURL + } } - hc := provider.NewHealthCheck(ps, testUrl, uint(groupOption.TestTimeout), uint(groupOption.Interval), groupOption.Lazy, expectedStatus) + hc := provider.NewHealthCheck(ps, groupOption.URL, uint(groupOption.TestTimeout), uint(groupOption.Interval), groupOption.Lazy, expectedStatus) pd, err := provider.NewCompatibleProvider(groupName, ps, hc) if err != nil { @@ -128,14 +118,17 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide providersMap[groupName] = pd } - if len(GroupUse) != 0 { - list, err := getProviders(providersMap, GroupUse) + if len(groupOption.Use) != 0 { + if groupOption.URL == "" { + groupOption.URL = C.DefaultTestURL + } + list, err := getProviders(providersMap, groupOption.Use) if err != nil { return nil, fmt.Errorf("%s: %w", groupName, err) } // different proxy groups use different test URL - addTestUrlToProviders(list, testUrl, expectedStatus, groupOption.Filter, uint(groupOption.Interval)) + addTestUrlToProviders(list, groupOption.URL, expectedStatus, groupOption.Filter, uint(groupOption.Interval)) providers = append(providers, list...) } else { diff --git a/adapter/provider/healthcheck.go b/adapter/provider/healthcheck.go index fbc1d14f..bfbcf8d9 100644 --- a/adapter/provider/healthcheck.go +++ b/adapter/provider/healthcheck.go @@ -211,8 +211,8 @@ func (hc *HealthCheck) close() { 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 + expectedStatus = nil + interval = 0 } if timeout == 0 { timeout = 5000 From 8d9eb1e53442449bb184266100b40329dc15d787 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sat, 24 Feb 2024 14:52:42 +0800 Subject: [PATCH 16/65] chore: get HealCheckURL from pd if groupOption URL is empty --- adapter/outboundgroup/parser.go | 19 +++++++++++++------ adapter/provider/provider.go | 8 ++++++++ constant/provider/interface.go | 1 + 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/adapter/outboundgroup/parser.go b/adapter/outboundgroup/parser.go index 2fa8d4ec..96b23eb2 100644 --- a/adapter/outboundgroup/parser.go +++ b/adapter/outboundgroup/parser.go @@ -119,20 +119,27 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide } if len(groupOption.Use) != 0 { - if groupOption.URL == "" { - groupOption.URL = C.DefaultTestURL - } list, err := getProviders(providersMap, groupOption.Use) if err != nil { return nil, fmt.Errorf("%s: %w", groupName, err) } + if groupOption.URL == "" { + for _, p := range list { + if p.HealthCheckURL() != "" { + groupOption.URL = p.HealthCheckURL() + } + break + } + + if groupOption.URL == "" { + groupOption.URL = C.DefaultTestURL + } + } + // different proxy groups use different test URL addTestUrlToProviders(list, groupOption.URL, expectedStatus, groupOption.Filter, uint(groupOption.Interval)) - providers = append(providers, list...) - } else { - groupOption.Filter = "" } var group C.ProxyAdapter diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index b591538b..b1209d22 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -106,6 +106,10 @@ func (pp *proxySetProvider) Touch() { pp.healthCheck.touch() } +func (pp *proxySetProvider) HealthCheckURL() string { + return pp.healthCheck.url +} + func (pp *proxySetProvider) RegisterHealthCheckTask(url string, expectedStatus utils.IntRanges[uint16], filter string, interval uint) { pp.healthCheck.registerHealthCheckTask(url, expectedStatus, filter, interval) } @@ -271,6 +275,10 @@ func (cp *compatibleProvider) Touch() { cp.healthCheck.touch() } +func (cp *compatibleProvider) HealthCheckURL() string { + return cp.healthCheck.url +} + func (cp *compatibleProvider) RegisterHealthCheckTask(url string, expectedStatus utils.IntRanges[uint16], filter string, interval uint) { cp.healthCheck.registerHealthCheckTask(url, expectedStatus, filter, interval) } diff --git a/constant/provider/interface.go b/constant/provider/interface.go index 809db9c5..f2b6939e 100644 --- a/constant/provider/interface.go +++ b/constant/provider/interface.go @@ -73,6 +73,7 @@ type ProxyProvider interface { HealthCheck() Version() uint32 RegisterHealthCheckTask(url string, expectedStatus utils.IntRanges[uint16], filter string, interval uint) + HealthCheckURL() string } // RuleProvider interface From 78b4b11f26cdc538863b49e05c3432a39dcb0420 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sun, 25 Feb 2024 20:42:01 +0800 Subject: [PATCH 17/65] chore: Update workflow --- .github/mihomo.service | 17 ++ .github/workflows/build.yml | 488 +++++++++++++++++------------------- Dockerfile | 2 +- 3 files changed, 249 insertions(+), 258 deletions(-) create mode 100644 .github/mihomo.service diff --git a/.github/mihomo.service b/.github/mihomo.service new file mode 100644 index 00000000..057c0236 --- /dev/null +++ b/.github/mihomo.service @@ -0,0 +1,17 @@ +[Unit] +Description=mihomo Daemon, Another Clash Kernel. +After=network.target NetworkManager.service systemd-networkd.service iwd.service + +[Service] +Type=simple +LimitNPROC=500 +LimitNOFILE=1000000 +CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH +AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH +Restart=always +ExecStartPre=/usr/bin/sleep 2s +ExecStart=/usr/local/bin/mihomo -d /etc/mihomo +ExecReload=/bin/kill -HUP $MAINPID + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 654e576f..ebbe9d0d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,314 +21,288 @@ concurrency: env: REGISTRY: docker.io jobs: - Build: - permissions: write-all + build: runs-on: ubuntu-latest strategy: - fail-fast: false matrix: - job: - - { - type: "WithoutCGO", - target: "linux-amd64 linux-amd64-compatible", - id: "1", - } - - { - type: "WithoutCGO", - target: "linux-armv5 linux-armv6 linux-armv7", - id: "2", - } - - { - type: "WithoutCGO", - target: "linux-arm64 linux-mips64 linux-mips64le", - id: "3", - } - - { - type: "WithoutCGO", - target: "linux-mips-softfloat linux-mips-hardfloat linux-mipsle-softfloat linux-mipsle-hardfloat", - id: "4", - } - - { type: "WithoutCGO", target: "linux-386 linux-riscv64 linux-loong64", id: "5" } - - { - type: "WithoutCGO", - target: "freebsd-386 freebsd-amd64 freebsd-arm64", - id: "6", - } - - { - type: "WithoutCGO", - target: "windows-amd64-compatible windows-amd64 windows-386", - id: "7", - } - - { - type: "WithoutCGO", - target: "windows-arm64 windows-arm32v7", - id: "8", - } - - { - type: "WithoutCGO", - target: "darwin-amd64 darwin-arm64", - id: "9", - } - - { - type: "WithoutCGO", - target: "darwin-amd64-compatible android-arm64", - id: "10", - } - # only for test - - { type: "WithoutCGO-GO120", target: "linux-amd64 linux-amd64-compatible",id: "1" } - # Go 1.20 is the last release that will run on any release of Windows 7, 8, Server 2008 and Server 2012. Go 1.21 will require at least Windows 10 or Server 2016. - - { type: "WithoutCGO-GO120", target: "windows-amd64-compatible windows-amd64 windows-386",id: "2" } - # Go 1.20 is the last release that will run on macOS 10.13 High Sierra or 10.14 Mojave. Go 1.21 will require macOS 10.15 Catalina or later. - - { type: "WithoutCGO-GO120", target: "darwin-amd64 darwin-arm64",id: "3" } - - { type: "WithoutCGO-GO120", target: "darwin-amd64-compatible android-arm64",id: "4" } -# - { type: "WithCGO", target: "windows/*", id: "1" } -# - { type: "WithCGO", target: "linux/386", id: "2" } -# - { type: "WithCGO", target: "linux/amd64", id: "3" } -# - { type: "WithCGO", target: "linux/arm64,linux/riscv64", id: "4" } -# - { type: "WithCGO", target: "linux/arm,", id: "5" } -# - { type: "WithCGO", target: "linux/arm-6,linux/arm-7", id: "6" } -# - { type: "WithCGO", target: "linux/mips,linux/mipsle", id: "7" } -# - { type: "WithCGO", target: "linux/mips64", id: "8" } -# - { type: "WithCGO", target: "linux/mips64le", id: "9" } -# - { type: "WithCGO", target: "darwin-10.16/*", id: "10" } -# - { type: "WithCGO", target: "android", id: "11" } + jobs: + - { goos: darwin, goarch: arm64, output: arm64 } + - { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-compatible } + - { goos: darwin, goarch: amd64, goamd64: v3, output: amd64 } + + - { goos: linux, goarch: '386', output: '386' } + - { goos: linux, goarch: amd64, goamd64: v1, output: amd64-compatible } + - { goos: linux, goarch: amd64, goamd64: v3, output: amd64 } + - { goos: linux, goarch: arm64, output: arm64 } + - { goos: linux, goarch: arm, goarm: '7', output: armv7 } + - { goos: linux, goarch: mips, mips: hardfloat, output: mips-hardfloat } + - { goos: linux, goarch: mips, mips: softfloat, output: mips-softfloat } + - { goos: linux, goarch: mipsle, mips: hardfloat, output: mipsle-hardfloat } + - { goos: linux, goarch: mipsle, mips: softfloat, output: mipsle-softfloat } + - { goos: linux, goarch: mips64, output: mips64 } + - { goos: linux, goarch: mips64le, output: mips64le } + - { goos: linux, goarch: loong64, output: loong64 } + - { goos: linux, goarch: riscv64, output: riscv64 } + - { goos: linux, goarch: s390x, output: s390x } + + - { goos: windows, goarch: '386', output: '386' } + - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible } + - { goos: windows, goarch: amd64, goamd64: v3, output: amd64 } + - { goos: windows, goarch: arm, goarm: '7', output: armv7 } + - { goos: windows, goarch: arm64, output: arm64 } + + - { goos: freebsd, goarch: '386', output: '386' } + - { goos: freebsd, goarch: amd64, goamd64: v1, output: amd64-compatible } + - { goos: freebsd, goarch: amd64, goamd64: v3, output: amd64 } + - { goos: freebsd, goarch: arm64, output: arm64 } + + - { goos: android, goarch: '386', ndk: i686-linux-android34, output: '386' } + - { goos: android, goarch: amd64, ndk: x86_64-linux-android34, output: amd64 } + - { goos: android, goarch: arm, ndk: armv7a-linux-androideabi34, output: armv7 } + - { goos: android, goarch: arm64, ndk: aarch64-linux-android34, output: arm64-v8 } + + - { goos: windows, goarch: '386', output: '386-go120', version: 20 } + - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, version: 20 } + - { goos: windows, goarch: amd64, goamd64: v3, output: amd64-go120, version: 20 } + + - { goos: darwin, goarch: arm64, output: arm64-go120, version: 20 } + - { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, version: 20 } + - { goos: darwin, goarch: amd64, goamd64: v3, output: amd64-go120, version: 20 } + + - { goos: linux, goarch: '386', output: '386-go120', version: 20 } + - { goos: linux, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, version: 20 } + - { goos: linux, goarch: amd64, goamd64: v3, output: amd64-go120, version: 20 } steps: - - name: Check out code into the Go module directory - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - name: Set variables - run: echo "VERSION=$(git rev-parse --short HEAD)" >> $GITHUB_ENV - shell: bash + - name: Set up Go1.22 + if: ${{ matrix.jobs.version != '20' }} + uses: actions/setup-go@v5 + with: + go-version: ^1.22 - - name: Set variables - if: ${{github.ref_name=='Alpha'}} - run: echo "VERSION=alpha-$(git rev-parse --short HEAD)" >> $GITHUB_ENV - shell: bash + - name: Set up Go1.20 + if: ${{ matrix.jobs.version == '20' }} + uses: actions/setup-go@v5 + with: + go-version: ^1.20 - - name: Set variables - if: ${{github.ref_name=='Meta'}} - run: echo "VERSION=meta-$(git rev-parse --short HEAD)" >> $GITHUB_ENV - shell: bash + - name: Set variables + if: ${{github.ref_name=='Alpha'}} + run: echo "VERSION=alpha-$(git rev-parse --short HEAD)" >> $GITHUB_ENV + shell: bash - - name: Set variables - if: ${{github.ref_name=='' || github.ref_type=='tag'}} - run: echo "VERSION=$(git describe --tags)" >> $GITHUB_ENV - shell: bash + - name: Set variables + if: ${{github.ref_name=='' || github.ref_type=='tag'}} + run: echo "VERSION=$(git describe --tags)" >> $GITHUB_ENV + shell: bash - - name: Set ENV - run: | - sudo timedatectl set-timezone "Asia/Shanghai" - echo "BUILDTIME=$(date)" >> $GITHUB_ENV - shell: bash + - name: Set Time Variable + run: | + echo "BUILDTIME=$(date)" >> $GITHUB_ENV + echo "CGO_ENABLED=0" >> $GITHUB_ENV + echo "BUILDTAG=-extldflags --static" >> $GITHUB_ENV - - name: Set ENV - run: | - echo "TAGS=with_gvisor" >> $GITHUB_ENV - echo "LDFLAGS=-X 'github.com/metacubex/mihomo/constant.Version=${VERSION}' -X 'github.com/metacubex/mihomo/constant.BuildTime=${BUILDTIME}' -w -s -buildid=" >> $GITHUB_ENV - echo "GOTOOLCHAIN=local" >> $GITHUB_ENV - shell: bash + - name: Setup NDK + if: ${{ matrix.jobs.goos == 'android' }} + uses: nttld/setup-ndk@v1 + id: setup-ndk + with: + ndk-version: r26c - - name: Setup Go - if: ${{ matrix.job.type!='WithoutCGO-GO120' }} - uses: actions/setup-go@v5 - with: - go-version: "1.22" - check-latest: true + - name: Set NDK path + if: ${{ matrix.jobs.goos == 'android' }} + run: | + echo "CC=${{steps.setup-ndk.outputs.ndk-path}}/toolchains/llvm/prebuilt/linux-x86_64/bin/${{matrix.jobs.ndk}}-clang" >> $GITHUB_ENV + echo "CGO_ENABLED=1" >> $GITHUB_ENV + echo "BUILDTAG=" >> $GITHUB_ENV - - name: Setup Go - if: ${{ matrix.job.type=='WithoutCGO-GO120' }} - uses: actions/setup-go@v5 - with: - go-version: "1.20" - check-latest: true + - name: build core + env: + GOOS: ${{matrix.jobs.goos}} + GOARCH: ${{matrix.jobs.goarch}} + GOAMD64: ${{matrix.jobs.goamd64}} + GOARM: ${{matrix.jobs.arm}} + GOMIPS: ${{matrix.jobs.mips}} + run: | + echo $CGO_ENABLED + go build -v -tags "with_gvisor" -trimpath -ldflags "${BUILDTAG} -X 'github.com/metacubex/mihomo/constant.Version=${VERSION}' -X 'github.com/metacubex/mihomo/constant.BuildTime=${BUILDTIME}' -w -s -buildid=" + if [ "${{matrix.jobs.goos}}" = "windows" ]; then + cp mihomo.exe mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}.exe + zip -r mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.zip mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}.exe + else + cp mihomo mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}} + gzip -c mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}} > mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.gz + rm mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}} + fi - - name: Test - if: ${{ matrix.job.id=='1' && matrix.job.type!='WithCGO' }} - run: | - go test ./... + - name: Create DEB package + if: ${{ matrix.jobs.goos == 'linux' && !contains(matrix.jobs.goarch, 'mips') }} + run: | + sudo apt-get install dpkg - - name: Build WithoutCGO - if: ${{ matrix.job.type!='WithCGO' }} - env: - NAME: mihomo - BINDIR: bin - run: make -j$(($(nproc) + 1)) ${{ matrix.job.target }} + mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/DEBIAN + mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/usr/bin + mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/etc/mihomo + mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/etc/systemd/system/ + mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/usr/share/licenses/mihomo - - uses: nttld/setup-ndk@v1 - if: ${{ matrix.job.type=='WithCGO' && matrix.job.target=='android' }} - id: setup-ndk - with: - ndk-version: r26b - add-to-path: true + cp mihomo mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/usr/bin/mihomo + cp LICENSE mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/usr/share/licenses/mihomo/ + cp .github/mihomo.service mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/etc/systemd/system/ - - name: Build Android - if: ${{ matrix.job.type=='WithCGO' && matrix.job.target=='android' }} - env: - ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} - run: | - mkdir bin - CC=${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android33-clang - CGO_ENABLED=1 CC=${CC} GOARCH=arm64 GOOS=android go build -tags ${TAGS} -trimpath -ldflags "${LDFLAGS}" -o bin/${NAME}-android-arm64 + cat > mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/etc/mihomo/config.yaml < mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/DEBIAN/control < + Homepage: https://wiki.metacubex.one/ + Description: The universal proxy platform. + EOF - - name: Build by xgo - if: ${{ matrix.job.type=='WithCGO' && matrix.job.target!='android' }} - env: - ANDROID_NDK_HOME: ${{ steps.setup-ndk.outputs.ndk-path }} - run: | - mkdir bin - xgo --targets="${{ matrix.job.target }}" --tags="${TAGS}" -ldflags="${LDFLAGS}" --out bin/${NAME} ./ + dpkg-deb -Z gzip --build mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION} - - name: Rename - if: ${{ matrix.job.type=='WithCGO' }} - run: | - cd bin - ls -la - cp ../.github/rename-cgo.sh ./ - bash ./rename-cgo.sh - rm ./rename-cgo.sh - ls -la - cd .. + - name: Convert DEB to RPM + if: ${{ matrix.jobs.goos == 'linux' && !contains(matrix.jobs.goarch, 'mips') }} + run: | + sudo apt-get install -y alien + alien --to-rpm --scripts mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.deb + mv mihomo*.rpm mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.rpm - - name: Rename - if: ${{ matrix.job.type=='WithoutCGO-GO120' }} - run: | - cd bin - ls -la - cp ../.github/rename-go120.sh ./ - bash ./rename-go120.sh - rm ./rename-go120.sh - ls -la - cd .. + - name: Convert DEB to PKG + if: ${{ matrix.jobs.goos == 'linux' && !contains(matrix.jobs.goarch, 'mips') }} + run: | + docker pull archlinux + docker run --rm -v ./:/mnt archlinux bash -c " + pacman -Syu pkgfile base-devel --noconfirm + curl -L https://github.com/helixarch/debtap/raw/master/debtap > /usr/bin/debtap + chmod 755 /usr/bin/debtap + debtap -u + debtap -Q /mnt/mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.deb + " + mv mihomo*.pkg.tar.zst mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.pkg.tar.zst - - name: Zip - if: ${{ success() }} - run: | - cd bin - ls -la - chmod +x * - cp ../.github/release.sh ./ - bash ./release.sh - rm ./release.sh - ls -la - cd .. + - name: Save version + run: | + echo ${VERSION} > version.txt + shell: bash - - name: Save version - run: echo ${VERSION} > bin/version.txt - shell: bash - - - uses: actions/upload-artifact@v4 - if: ${{ success() }} - with: - name: artifact-${{ matrix.job.type }}-${{ matrix.job.target }} - path: bin/ + - name: Archive production artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.jobs.goos }}-${{ matrix.jobs.output }} + path: | + mihomo*.gz + mihomo*.deb + mihomo*.rpm + mihomo*.pkg.tar.zst + mihomo*.zip + version.txt Upload-Prerelease: permissions: write-all if: ${{ github.ref_type == 'branch' && !startsWith(github.event_name, 'pull_request') }} - needs: [Build] + needs: [build] runs-on: ubuntu-latest steps: - - uses: actions/download-artifact@v4 - with: - path: bin/ - merge-multiple: true + - name: Download all workflow run artifacts + uses: actions/download-artifact@v4 + with: + path: bin/ + merge-multiple: true - - name: Display structure of downloaded files - run: ls -R - working-directory: bin + - name: Delete current release assets + uses: 8Mi-Tech/delete-release-assets-action@main + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + tag: Prerelease-${{ github.ref_name }} + deleteOnlyFromDrafts: false + - name: Set Env + run: | + echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV + shell: bash - - name: Delete current release assets - uses: 8Mi-Tech/delete-release-assets-action@main - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - tag: Prerelease-${{ github.ref_name }} - deleteOnlyFromDrafts: false - - - name: Set Env - run: | - echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV - shell: bash - - - name: Tag Repo - uses: richardsimko/update-tag@v1 - with: - tag_name: Prerelease-${{ github.ref_name }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Tag Repo + uses: richardsimko/update-tag@v1 + with: + tag_name: Prerelease-${{ github.ref_name }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - run: | - cat > release.txt << 'EOF' - Release created at ${{ env.BUILDTIME }} - 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 + - run: | + cat > release.txt << 'EOF' + Release created at ${{ env.BUILDTIME }} + 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 - - name: Upload Prerelease - uses: softprops/action-gh-release@v1 - if: ${{ success() }} - with: - tag_name: Prerelease-${{ github.ref_name }} - files: | - bin/* - prerelease: true - generate_release_notes: true - body_path: release.txt + - name: Upload Prerelease + uses: softprops/action-gh-release@v1 + if: ${{ success() }} + with: + tag_name: Prerelease-${{ github.ref_name }} + files: | + bin/* + prerelease: true + generate_release_notes: true + body_path: release.txt Upload-Release: permissions: write-all if: ${{ github.ref_type=='tag' }} - needs: [Build] + needs: [build] runs-on: ubuntu-latest steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 + - name: Checkout + uses: actions/checkout@v4 + 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: 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: | + - name: Generate release notes + run: | cp ./.github/genReleaseNote.sh ./ bash ./genReleaseNote.sh -v ${PREVERSION}...${CURRENTVERSION} rm ./genReleaseNote.sh - - uses: actions/download-artifact@v4 - with: - path: bin/ - merge-multiple: true + - uses: actions/download-artifact@v4 + with: + path: bin/ + merge-multiple: true - - name: Display structure of downloaded files - run: ls -R - working-directory: bin + - name: Display structure of downloaded files + run: ls -R + working-directory: bin - - name: Upload Release - uses: softprops/action-gh-release@v1 - if: ${{ success() }} - with: - tag_name: ${{ github.ref_name }} - files: bin/* - generate_release_notes: true - body_path: release.md + - name: Upload Release + uses: softprops/action-gh-release@v1 + if: ${{ success() }} + with: + tag_name: ${{ github.ref_name }} + files: bin/* + generate_release_notes: true + body_path: release.md Docker: if: ${{ !startsWith(github.event_name, 'pull_request') }} permissions: write-all - needs: [Build] + needs: [build] runs-on: ubuntu-latest steps: - name: Checkout repository diff --git a/Dockerfile b/Dockerfile index c9cd56b7..64b33cf7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,7 +12,7 @@ COPY docker/file-name.sh /mihomo/file-name.sh WORKDIR /mihomo COPY bin/ bin/ RUN FILE_NAME=`sh file-name.sh` && echo $FILE_NAME && \ - FILE_NAME=`ls bin/ | egrep "$FILE_NAME.*"|awk NR==1` && echo $FILE_NAME && \ + FILE_NAME=`ls bin/ | egrep "$FILE_NAME.gz"|awk NR==1` && echo $FILE_NAME && \ mv bin/$FILE_NAME mihomo.gz && gzip -d mihomo.gz && echo "$FILE_NAME" > /mihomo-config/test FROM alpine:latest LABEL org.opencontainers.image.source="https://github.com/MetaCubeX/mihomo" From 0619c752763b5357e96fc0c83e1ef0fbe66d9c0d Mon Sep 17 00:00:00 2001 From: Skyxim Date: Mon, 26 Feb 2024 06:05:06 +0000 Subject: [PATCH 18/65] fix: url format error when host is IPv6 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Bug] 使用IPV6地址+vmess+http伪装的配置引起内核panic MetaCubeX/mihomo#1063 --- transport/vmess/http.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport/vmess/http.go b/transport/vmess/http.go index c77a7e9d..6da9759e 100644 --- a/transport/vmess/http.go +++ b/transport/vmess/http.go @@ -60,7 +60,7 @@ func (hc *httpConn) Write(b []byte) (int, error) { host = header[fastrand.Intn(len(header))] } - u := fmt.Sprintf("http://%s%s", host, path) + u := fmt.Sprintf("http://%s%s", net.JoinHostPort(host, "80"), path) req, _ := http.NewRequest(utils.EmptyOr(hc.cfg.Method, http.MethodGet), u, bytes.NewBuffer(b)) for key, list := range hc.cfg.Headers { req.Header.Set(key, list[fastrand.Intn(len(list))]) From e58294198c158af8519731db012c07a17cfa4b96 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Tue, 27 Feb 2024 02:14:18 +0800 Subject: [PATCH 19/65] chore: Distinguish between abi1.0 and abi2.0 of loongarch64 --- .github/mihomo.service | 2 +- .github/workflows/build.yml | 33 +++++++++++++++++++++++++++------ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/.github/mihomo.service b/.github/mihomo.service index 057c0236..a884059f 100644 --- a/.github/mihomo.service +++ b/.github/mihomo.service @@ -10,7 +10,7 @@ CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIM AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH Restart=always ExecStartPre=/usr/bin/sleep 2s -ExecStart=/usr/local/bin/mihomo -d /etc/mihomo +ExecStart=/usr/bin/mihomo -d /etc/mihomo ExecReload=/bin/kill -HUP $MAINPID [Install] diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ebbe9d0d..c9ead5e0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -41,7 +41,8 @@ jobs: - { goos: linux, goarch: mipsle, mips: softfloat, output: mipsle-softfloat } - { goos: linux, goarch: mips64, output: mips64 } - { goos: linux, goarch: mips64le, output: mips64le } - - { goos: linux, goarch: loong64, output: loong64 } + - { goos: linux, goarch: loong64, output: loong64-abi1, abi: '1' } + - { goos: linux, goarch: loong64, output: loong64-abi2, abi: '2' } - { goos: linux, goarch: riscv64, output: riscv64 } - { goos: linux, goarch: s390x, output: s390x } @@ -77,17 +78,33 @@ jobs: - uses: actions/checkout@v4 - name: Set up Go1.22 - if: ${{ matrix.jobs.version != '20' }} + if: ${{ matrix.jobs.version != '20' && matrix.jobs.goarch != 'loong64' }} uses: actions/setup-go@v5 with: go-version: ^1.22 - name: Set up Go1.20 - if: ${{ matrix.jobs.version == '20' }} + if: ${{ matrix.jobs.version == '20' && matrix.jobs.goarch != 'loong64' }} uses: actions/setup-go@v5 with: go-version: ^1.20 + - name: Set up Go1.21 loongarch abi1 + if: ${{ matrix.jobs.goarch == 'loong64' && matrix.jobs.abi == '1' }} + run: | + wget -q https://github.com/xishang0128/loongarch64-golang/releases/download/1.21.5/go1.21.5.linux-amd64-abi1.tar.gz + sudo tar zxf go1.21.5.linux-amd64-abi1.tar.gz + sudo rm -rf /opt/hostedtoolcache/go/1.20.14/x64/* + sudo cp -r go/* /opt/hostedtoolcache/go/1.20.14/x64 + + - name: Set up Go1.21 loongarch abi2 + if: ${{ matrix.jobs.goarch == 'loong64' && matrix.jobs.abi == '2' }} + run: | + wget -q https://github.com/xishang0128/loongarch64-golang/releases/download/1.21.5/go1.21.5.linux-amd64-abi2.tar.gz + sudo tar zxf go1.21.5.linux-amd64-abi2.tar.gz + sudo rm -rf /opt/hostedtoolcache/go/1.20.14/x64/* + sudo cp -r go/* /opt/hostedtoolcache/go/1.20.14/x64 + - name: Set variables if: ${{github.ref_name=='Alpha'}} run: echo "VERSION=alpha-$(git rev-parse --short HEAD)" >> $GITHUB_ENV @@ -141,7 +158,11 @@ jobs: if: ${{ matrix.jobs.goos == 'linux' && !contains(matrix.jobs.goarch, 'mips') }} run: | sudo apt-get install dpkg - + if [ "${{matrix.jobs.goarch}}" = "loong64" ]; then + ARCH=loongarch64 + else + ARCH=${{matrix.jobs.goarch}} + fi mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/DEBIAN mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/usr/bin mkdir -p mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}/etc/mihomo @@ -162,7 +183,7 @@ jobs: Version: 1.18.2-${VERSION} Section: Priority: extra - Architecture: ${{matrix.jobs.goarch}} + Architecture: ${ARCH} Maintainer: MetaCubeX Homepage: https://wiki.metacubex.one/ Description: The universal proxy platform. @@ -178,7 +199,7 @@ jobs: mv mihomo*.rpm mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.rpm - name: Convert DEB to PKG - if: ${{ matrix.jobs.goos == 'linux' && !contains(matrix.jobs.goarch, 'mips') }} + if: ${{ matrix.jobs.goos == 'linux' && !contains(matrix.jobs.goarch, 'mips') && !contains(matrix.jobs.goarch, 'loong64') }} run: | docker pull archlinux docker run --rm -v ./:/mnt archlinux bash -c " From 41e3571e1d94fe9cab2706522850c98e1c919f0c Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 27 Feb 2024 14:26:49 +0800 Subject: [PATCH 20/65] ci: let go120 build work --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c9ead5e0..8e8a408b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -81,13 +81,13 @@ jobs: if: ${{ matrix.jobs.version != '20' && matrix.jobs.goarch != 'loong64' }} uses: actions/setup-go@v5 with: - go-version: ^1.22 + go-version: '1.22' - name: Set up Go1.20 if: ${{ matrix.jobs.version == '20' && matrix.jobs.goarch != 'loong64' }} uses: actions/setup-go@v5 with: - go-version: ^1.20 + go-version: '1.20' - name: Set up Go1.21 loongarch abi1 if: ${{ matrix.jobs.goarch == 'loong64' && matrix.jobs.abi == '1' }} From 092e53586e700b2cb720fb40e6876150622a1875 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 27 Feb 2024 15:21:30 +0800 Subject: [PATCH 21/65] ci: better build.yml --- .github/workflows/build.yml | 40 ++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8e8a408b..b655455e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,7 +31,7 @@ jobs: - { goos: darwin, goarch: amd64, goamd64: v3, output: amd64 } - { goos: linux, goarch: '386', output: '386' } - - { goos: linux, goarch: amd64, goamd64: v1, output: amd64-compatible } + - { goos: linux, goarch: amd64, goamd64: v1, output: amd64-compatible, test: test } - { goos: linux, goarch: amd64, goamd64: v3, output: amd64 } - { goos: linux, goarch: arm64, output: arm64 } - { goos: linux, goarch: arm, goarm: '7', output: armv7 } @@ -62,32 +62,35 @@ jobs: - { goos: android, goarch: arm, ndk: armv7a-linux-androideabi34, output: armv7 } - { goos: android, goarch: arm64, ndk: aarch64-linux-android34, output: arm64-v8 } - - { goos: windows, goarch: '386', output: '386-go120', version: 20 } - - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, version: 20 } - - { goos: windows, goarch: amd64, goamd64: v3, output: amd64-go120, version: 20 } + # Go 1.20 is the last release that will run on any release of Windows 7, 8, Server 2008 and Server 2012. Go 1.21 will require at least Windows 10 or Server 2016. + - { goos: windows, goarch: '386', output: '386-go120', goversion: '1.20' } + - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, goversion: '1.20' } + - { goos: windows, goarch: amd64, goamd64: v3, output: amd64-go120, goversion: '1.20' } - - { goos: darwin, goarch: arm64, output: arm64-go120, version: 20 } - - { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, version: 20 } - - { goos: darwin, goarch: amd64, goamd64: v3, output: amd64-go120, version: 20 } + # Go 1.20 is the last release that will run on macOS 10.13 High Sierra or 10.14 Mojave. Go 1.21 will require macOS 10.15 Catalina or later. + - { goos: darwin, goarch: arm64, output: arm64-go120, goversion: '1.20' } + - { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, goversion: '1.20' } + - { goos: darwin, goarch: amd64, goamd64: v3, output: amd64-go120, goversion: '1.20' } - - { goos: linux, goarch: '386', output: '386-go120', version: 20 } - - { goos: linux, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, version: 20 } - - { goos: linux, goarch: amd64, goamd64: v3, output: amd64-go120, version: 20 } + # only for test + - { goos: linux, goarch: '386', output: '386-go120', goversion: '1.20' } + - { goos: linux, goarch: amd64, goamd64: v1, output: amd64-compatible-go120, goversion: '1.20', test: test } + - { goos: linux, goarch: amd64, goamd64: v3, output: amd64-go120, goversion: '1.20' } steps: - uses: actions/checkout@v4 - - name: Set up Go1.22 - if: ${{ matrix.jobs.version != '20' && matrix.jobs.goarch != 'loong64' }} + - name: Set up Go + if: ${{ matrix.jobs.goversion == '' && matrix.jobs.goarch != 'loong64' }} uses: actions/setup-go@v5 with: go-version: '1.22' - - name: Set up Go1.20 - if: ${{ matrix.jobs.version == '20' && matrix.jobs.goarch != 'loong64' }} + - name: Set up Go + if: ${{ matrix.jobs.goversion != '' && matrix.jobs.goarch != 'loong64' }} uses: actions/setup-go@v5 with: - go-version: '1.20' + go-version: ${{ matrix.jobs.goversion }} - name: Set up Go1.21 loongarch abi1 if: ${{ matrix.jobs.goarch == 'loong64' && matrix.jobs.abi == '1' }} @@ -135,7 +138,12 @@ jobs: echo "CGO_ENABLED=1" >> $GITHUB_ENV echo "BUILDTAG=" >> $GITHUB_ENV - - name: build core + - name: Test + if: ${{ matrix.jobs.test == 'test' }} + run: | + go test ./... + + - name: Build core env: GOOS: ${{matrix.jobs.goos}} GOARCH: ${{matrix.jobs.goarch}} From f0bc68585a27d730bb3dc16eaeeeab5ad2efeefb Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Tue, 27 Feb 2024 23:11:56 +0800 Subject: [PATCH 22/65] chore: Update workflow --- .github/workflows/build.yml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b655455e..30abd89d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -96,17 +96,15 @@ jobs: if: ${{ matrix.jobs.goarch == 'loong64' && matrix.jobs.abi == '1' }} run: | wget -q https://github.com/xishang0128/loongarch64-golang/releases/download/1.21.5/go1.21.5.linux-amd64-abi1.tar.gz - sudo tar zxf go1.21.5.linux-amd64-abi1.tar.gz - sudo rm -rf /opt/hostedtoolcache/go/1.20.14/x64/* - sudo cp -r go/* /opt/hostedtoolcache/go/1.20.14/x64 + sudo tar zxf go1.21.5.linux-amd64-abi1.tar.gz -C /usr/local + echo "/usr/local/go/bin" >> $GITHUB_PATH - name: Set up Go1.21 loongarch abi2 if: ${{ matrix.jobs.goarch == 'loong64' && matrix.jobs.abi == '2' }} run: | wget -q https://github.com/xishang0128/loongarch64-golang/releases/download/1.21.5/go1.21.5.linux-amd64-abi2.tar.gz - sudo tar zxf go1.21.5.linux-amd64-abi2.tar.gz - sudo rm -rf /opt/hostedtoolcache/go/1.20.14/x64/* - sudo cp -r go/* /opt/hostedtoolcache/go/1.20.14/x64 + sudo tar zxf go1.21.5.linux-amd64-abi2.tar.gz -C /usr/local + echo "/usr/local/go/bin" >> $GITHUB_PATH - name: Set variables if: ${{github.ref_name=='Alpha'}} @@ -166,7 +164,7 @@ jobs: if: ${{ matrix.jobs.goos == 'linux' && !contains(matrix.jobs.goarch, 'mips') }} run: | sudo apt-get install dpkg - if [ "${{matrix.jobs.goarch}}" = "loong64" ]; then + if [ "${{matrix.jobs.abi}}" = "1" ]; then ARCH=loongarch64 else ARCH=${{matrix.jobs.goarch}} From 7eb16a098a92e43c4a8871c0103d670462252bbc Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 28 Feb 2024 11:14:10 +0800 Subject: [PATCH 23/65] chore: upgrade dependencies --- adapter/inbound/listen.go | 2 +- component/dialer/tfo.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/adapter/inbound/listen.go b/adapter/inbound/listen.go index 8b7b5fb2..18dc1bc2 100644 --- a/adapter/inbound/listen.go +++ b/adapter/inbound/listen.go @@ -4,7 +4,7 @@ import ( "context" "net" - "github.com/sagernet/tfo-go" + "github.com/metacubex/tfo-go" ) var ( diff --git a/component/dialer/tfo.go b/component/dialer/tfo.go index 950bdfe4..228e9689 100644 --- a/component/dialer/tfo.go +++ b/component/dialer/tfo.go @@ -6,7 +6,7 @@ import ( "net" "time" - "github.com/sagernet/tfo-go" + "github.com/metacubex/tfo-go" ) type tfoConn struct { diff --git a/go.mod b/go.mod index bba88573..7c28d2bf 100644 --- a/go.mod +++ b/go.mod @@ -26,6 +26,7 @@ require ( github.com/metacubex/sing-tun v0.2.1-0.20240214100323-23e40bfb9067 github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f github.com/metacubex/sing-wireguard v0.0.0-20231209125515-0594297f7232 + github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 github.com/miekg/dns v1.1.57 github.com/mroth/weightedrand/v2 v2.1.0 github.com/openacid/low v0.1.21 @@ -36,7 +37,6 @@ require ( github.com/sagernet/sing v0.3.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 github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e github.com/samber/lo v1.39.0 diff --git a/go.sum b/go.sum index 4e41bf44..957b30de 100644 --- a/go.sum +++ b/go.sum @@ -120,6 +120,8 @@ github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f h1:QjXrHKbT 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= github.com/metacubex/sing-wireguard v0.0.0-20231209125515-0594297f7232/go.mod h1:NGCrBZ+fUmp81yaA1kVskcNWBnwl5z4UHxz47A01zm8= +github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 h1:as/aO/fM8nv4W4pOr9EETP6kV/Oaujk3fUNyQSJK61c= +github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= github.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU= @@ -161,8 +163,6 @@ github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnV github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= -github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6 h1:z3SJQhVyU63FT26Wn/UByW6b7q8QKB0ZkPqsyqcz2PI= -github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6/go.mod h1:73xRZuxwkFk4aiLw28hG8W6o9cr2UPrGL9pdY2UTbvY= github.com/sagernet/utls v1.5.4 h1:KmsEGbB2dKUtCNC+44NwAdNAqnqQ6GA4pTO0Yik56co= github.com/sagernet/utls v1.5.4/go.mod h1:CTGxPWExIloRipK3XFpYv0OVyhO8kk3XCGW/ieyTh1s= github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e h1:iGH0RMv2FzELOFNFQtvsxH7NPmlo7X5JizEK51UCojo= From d27340867f2a71a4bb51f4f48ff7d9830031d110 Mon Sep 17 00:00:00 2001 From: H1JK Date: Sat, 2 Mar 2024 17:41:04 +0800 Subject: [PATCH 24/65] chore: Add GeoIP result to metadata --- component/mmdb/reader.go | 9 +++++---- constant/metadata.go | 1 + rules/common/geoip.go | 34 +++++++++++++++++++++++++--------- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/component/mmdb/reader.go b/component/mmdb/reader.go index 4db53d4f..6247cd8a 100644 --- a/component/mmdb/reader.go +++ b/component/mmdb/reader.go @@ -5,7 +5,6 @@ import ( "net" "github.com/oschwald/maxminddb-golang" - "github.com/sagernet/sing/common" ) type geoip2Country struct { @@ -44,9 +43,11 @@ func (r Reader) LookupCode(ipAddress net.IP) []string { case string: return []string{record} case []any: // lookup returned type of slice is []any - return common.Map(record, func(it any) string { - return it.(string) - }) + result := make([]string, 0, len(record)) + for _, item := range record { + result = append(result, item.(string)) + } + return result } return []string{} diff --git a/constant/metadata.go b/constant/metadata.go index 6df6ff43..bf0fa281 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -133,6 +133,7 @@ type Metadata struct { Type Type `json:"type"` SrcIP netip.Addr `json:"sourceIP"` DstIP netip.Addr `json:"destinationIP"` + DstGeoIP []string `json:"destinationGeoIP"` // can be nil if never queried, empty slice if got no result SrcPort uint16 `json:"sourcePort,string"` // `,string` is used to compatible with old version json output DstPort uint16 `json:"destinationPort,string"` // `,string` is used to compatible with old version json output InIP netip.Addr `json:"inboundIP"` diff --git a/rules/common/geoip.go b/rules/common/geoip.go index ebca1d16..2a891313 100644 --- a/rules/common/geoip.go +++ b/rules/common/geoip.go @@ -21,6 +21,8 @@ type GEOIP struct { recodeSize int } +var _ C.Rule = (*GEOIP)(nil) + func (g *GEOIP) RuleType() C.RuleType { return C.GEOIP } @@ -31,7 +33,7 @@ func (g *GEOIP) Match(metadata *C.Metadata) (bool, string) { return false, "" } - if strings.EqualFold(g.country, "LAN") { + if g.country == "lan" { return ip.IsPrivate() || ip.IsUnspecified() || ip.IsLoopback() || @@ -39,16 +41,31 @@ func (g *GEOIP) Match(metadata *C.Metadata) (bool, string) { ip.IsLinkLocalUnicast() || resolver.IsFakeBroadcastIP(ip), g.adapter } + + for _, code := range metadata.DstGeoIP { + if g.country == code { + return true, g.adapter + } + } + if !C.GeodataMode { - codes := mmdb.Instance().LookupCode(ip.AsSlice()) - for _, code := range codes { - if strings.EqualFold(code, g.country) { + if metadata.DstGeoIP != nil { + return false, g.adapter + } + metadata.DstGeoIP = mmdb.Instance().LookupCode(ip.AsSlice()) + for _, code := range metadata.DstGeoIP { + if g.country == code { return true, g.adapter } } return false, g.adapter } - return g.geoIPMatcher.Match(ip), g.adapter + + match := g.geoIPMatcher.Match(ip) + if match { + metadata.DstGeoIP = append(metadata.DstGeoIP, g.country) + } + return match, g.adapter } func (g *GEOIP) Adapter() string { @@ -80,8 +97,9 @@ func NewGEOIP(country string, adapter string, noResolveIP bool) (*GEOIP, error) log.Errorln("can't initial GeoIP: %s", err) return nil, err } + country = strings.ToLower(country) - if !C.GeodataMode || strings.EqualFold(country, "LAN") { + if !C.GeodataMode || country == "lan" { geoip := &GEOIP{ Base: &Base{}, country: country, @@ -93,7 +111,7 @@ func NewGEOIP(country string, adapter string, noResolveIP bool) (*GEOIP, error) geoIPMatcher, size, err := geodata.LoadGeoIPMatcher(country) if err != nil { - return nil, fmt.Errorf("[GeoIP] %s", err.Error()) + return nil, fmt.Errorf("[GeoIP] %w", err) } log.Infoln("Start initial GeoIP rule %s => %s, records: %d", country, adapter, size) @@ -107,5 +125,3 @@ func NewGEOIP(country string, adapter string, noResolveIP bool) (*GEOIP, error) } return geoip, nil } - -//var _ C.Rule = (*GEOIP)(nil) From 3ec23c1fc52bf65e5f9cd0b5b94ccdec139c8a84 Mon Sep 17 00:00:00 2001 From: sduoduo233 <85996970+sduoduo233@users.noreply.github.com> Date: Mon, 4 Mar 2024 18:21:50 +0800 Subject: [PATCH 25/65] feat: Add DNS outbound to hijack DNS packets (#1078) --- adapter/outbound/dns.go | 143 ++++++++++++++++++++++++++++++++++++++++ adapter/parser.go | 7 ++ constant/adapters.go | 1 + 3 files changed, 151 insertions(+) create mode 100644 adapter/outbound/dns.go diff --git a/adapter/outbound/dns.go b/adapter/outbound/dns.go new file mode 100644 index 00000000..14eaf581 --- /dev/null +++ b/adapter/outbound/dns.go @@ -0,0 +1,143 @@ +package outbound + +import ( + "context" + "fmt" + "net" + "net/netip" + "time" + + "github.com/metacubex/mihomo/component/dialer" + "github.com/metacubex/mihomo/component/resolver" + C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/log" + D "github.com/miekg/dns" +) + +type Dns struct { + *Base +} + +type DnsOption struct { + BasicOption + Name string `proxy:"name"` +} + +// DialContext implements C.ProxyAdapter +func (d *Dns) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { + return nil, fmt.Errorf("dns outbound does not support tcp") +} + +// ListenPacketContext implements C.ProxyAdapter +func (d *Dns) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { + log.Debugln("[DNS] hijack udp:%s from %s", metadata.RemoteAddress(), metadata.SourceAddrPort()) + + return newPacketConn(&dnsPacketConn{ + response: make(chan []byte), + doneReading: make(chan int), + }, d), nil +} + +// dnsPacketConn implements net.PacketConn +type dnsPacketConn struct { + response chan []byte + writeTo net.Addr + doneReading chan int +} + +func (d *dnsPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + buf := <-d.response + + log.Debugln("[DNS] hijack ReadFrom, len %d", len(buf)) + + if buf != nil { + n := copy(p, buf) + return n, d.writeTo, nil + } + + return 0, nil, fmt.Errorf("read from closed dns packet conn") +} + +func (d *dnsPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + log.Debugln("[DNS] hijack WriteTo %s, len %d", addr.String(), len(p)) + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + defer cancel() + + buf, err := RelayDnsPacket(ctx, p, make([]byte, 4096)) + if err != nil { + log.Warnln("[DNS] dns hijack: relay dns packet: %s", err) + return 0, err + } + + d.writeTo = addr + d.response <- buf + + return len(p), nil +} + +func (d *dnsPacketConn) Close() error { + close(d.response) + return nil +} + +func (*dnsPacketConn) LocalAddr() net.Addr { + return net.UDPAddrFromAddrPort(netip.MustParseAddrPort("127.0.0.1:53")) +} + +func (*dnsPacketConn) SetDeadline(t time.Time) error { + return nil +} + +func (*dnsPacketConn) SetReadDeadline(t time.Time) error { + return nil +} + +func (*dnsPacketConn) SetWriteDeadline(t time.Time) error { + return nil +} + +func NewDnsWithOption(option DnsOption) *Dns { + return &Dns{ + Base: &Base{ + name: option.Name, + tp: C.Direct, + udp: true, + tfo: option.TFO, + mpTcp: option.MPTCP, + iface: option.Interface, + rmark: option.RoutingMark, + prefer: C.NewDNSPrefer(option.IPVersion), + }, + } +} + +// copied from listener/sing_mux/dns.go +func RelayDnsPacket(ctx context.Context, payload []byte, target []byte) ([]byte, error) { + msg := &D.Msg{} + if err := msg.Unpack(payload); err != nil { + return nil, err + } + + r, err := resolver.ServeMsg(ctx, msg) + if err != nil { + m := new(D.Msg) + m.SetRcode(msg, D.RcodeServerFailure) + return m.PackBuffer(target) + } + + r.SetRcode(msg, r.Rcode) + r.Compress = true + return r.PackBuffer(target) +} + +func NewDns() *Dns { + return &Dns{ + Base: &Base{ + name: "DNS", + tp: C.Dns, + udp: true, + prefer: C.DualStack, + }, + } +} diff --git a/adapter/parser.go b/adapter/parser.go index 1d363c1f..fa94708d 100644 --- a/adapter/parser.go +++ b/adapter/parser.go @@ -120,6 +120,13 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) { break } proxy = outbound.NewDirectWithOption(*directOption) + case "dns": + dnsOptions := &outbound.DnsOption{} + err = decoder.Decode(mapping, dnsOptions) + if err != nil { + break + } + proxy = outbound.NewDnsWithOption(*dnsOptions) case "reject": rejectOption := &outbound.RejectOption{} err = decoder.Decode(mapping, rejectOption) diff --git a/constant/adapters.go b/constant/adapters.go index 83c8223a..09c437f5 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -21,6 +21,7 @@ const ( RejectDrop Compatible Pass + Dns Relay Selector From e86749731550433431cd6f07ed5075610dd69dbc Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 4 Mar 2024 19:00:19 +0800 Subject: [PATCH 26/65] chore: rebuild DNS outbound code --- adapter/outbound/dns.go | 89 ++++++++++++++++++++++++----------------- 1 file changed, 53 insertions(+), 36 deletions(-) diff --git a/adapter/outbound/dns.go b/adapter/outbound/dns.go index 14eaf581..94819749 100644 --- a/adapter/outbound/dns.go +++ b/adapter/outbound/dns.go @@ -7,10 +7,12 @@ import ( "net/netip" "time" + "github.com/metacubex/mihomo/common/pool" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/resolver" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" + D "github.com/miekg/dns" ) @@ -32,52 +34,78 @@ func (d *Dns) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dia func (d *Dns) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { log.Debugln("[DNS] hijack udp:%s from %s", metadata.RemoteAddress(), metadata.SourceAddrPort()) + ctx, cancel := context.WithCancel(context.Background()) + return newPacketConn(&dnsPacketConn{ - response: make(chan []byte), - doneReading: make(chan int), + response: make(chan dnsPacket, 1), + ctx: ctx, + cancel: cancel, }, d), nil } +type dnsPacket struct { + data []byte + put func() + addr net.Addr +} + // dnsPacketConn implements net.PacketConn type dnsPacketConn struct { - response chan []byte - writeTo net.Addr - doneReading chan int + response chan dnsPacket + ctx context.Context + cancel context.CancelFunc +} + +func (d *dnsPacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) { + select { + case packet := <-d.response: + return packet.data, packet.put, packet.addr, nil + case <-d.ctx.Done(): + return nil, nil, nil, net.ErrClosed + } } func (d *dnsPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - buf := <-d.response - - log.Debugln("[DNS] hijack ReadFrom, len %d", len(buf)) - - if buf != nil { - n := copy(p, buf) - return n, d.writeTo, nil + select { + case packet := <-d.response: + n = copy(p, packet.data) + if packet.put != nil { + packet.put() + } + return n, packet.addr, nil + case <-d.ctx.Done(): + return 0, nil, net.ErrClosed } - - return 0, nil, fmt.Errorf("read from closed dns packet conn") } func (d *dnsPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - log.Debugln("[DNS] hijack WriteTo %s, len %d", addr.String(), len(p)) - - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) + ctx, cancel := context.WithTimeout(d.ctx, time.Second*5) defer cancel() - buf, err := RelayDnsPacket(ctx, p, make([]byte, 4096)) + buf := pool.Get(2048) + put := func() { _ = pool.Put(buf) } + buf, err = RelayDnsPacket(ctx, p, buf) if err != nil { - log.Warnln("[DNS] dns hijack: relay dns packet: %s", err) + put() return 0, err } - d.writeTo = addr - d.response <- buf - - return len(p), nil + packet := dnsPacket{ + data: buf, + put: put, + addr: addr, + } + select { + case d.response <- packet: + return len(p), nil + case <-d.ctx.Done(): + put() + return 0, net.ErrClosed + } } func (d *dnsPacketConn) Close() error { - close(d.response) + d.cancel() return nil } @@ -101,7 +129,7 @@ func NewDnsWithOption(option DnsOption) *Dns { return &Dns{ Base: &Base{ name: option.Name, - tp: C.Direct, + tp: C.Dns, udp: true, tfo: option.TFO, mpTcp: option.MPTCP, @@ -130,14 +158,3 @@ func RelayDnsPacket(ctx context.Context, payload []byte, target []byte) ([]byte, r.Compress = true return r.PackBuffer(target) } - -func NewDns() *Dns { - return &Dns{ - Base: &Base{ - name: "DNS", - tp: C.Dns, - udp: true, - prefer: C.DualStack, - }, - } -} From 69bf434e2c51f268c14801697e6621f461ca1adb Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 4 Mar 2024 19:14:40 +0800 Subject: [PATCH 27/65] chore: vlessPacketConn should wrap ThreadSafePacketConn --- adapter/outbound/vless.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index ceeb52a5..43b4aa21 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -373,7 +373,7 @@ func (v *Vless) ListenPacketOnStreamConn(ctx context.Context, c net.Conn, metada }, M.SocksaddrFromNet(metadata.UDPAddr())), ), v), nil } - return newPacketConn(&vlessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil + return newPacketConn(N.NewThreadSafePacketConn(&vlessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}), v), nil } // SupportUOT implements C.ProxyAdapter From fe4acebb8b7ed56f52a287836fd59ad3fae777a9 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Mon, 4 Mar 2024 20:02:09 +0800 Subject: [PATCH 28/65] chore: Supplement type --- constant/adapters.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/constant/adapters.go b/constant/adapters.go index 09c437f5..105a7904 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -185,6 +185,8 @@ func (at AdapterType) String() string { return "Compatible" case Pass: return "Pass" + case Dns: + return "Dns" case Shadowsocks: return "Shadowsocks" case ShadowsocksR: From 8b9813079b85e5020dbf5ddb8f321c647944c136 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 4 Mar 2024 22:12:08 +0800 Subject: [PATCH 29/65] chore: share RelayDnsPacket function code --- adapter/outbound/dns.go | 34 ++++---------- component/resolver/relay.go | 88 +++++++++++++++++++++++++++++++++++++ listener/sing_tun/dns.go | 88 +++---------------------------------- 3 files changed, 102 insertions(+), 108 deletions(-) create mode 100644 component/resolver/relay.go diff --git a/adapter/outbound/dns.go b/adapter/outbound/dns.go index 94819749..405392a1 100644 --- a/adapter/outbound/dns.go +++ b/adapter/outbound/dns.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "net" - "net/netip" "time" "github.com/metacubex/mihomo/common/pool" @@ -12,8 +11,6 @@ import ( "github.com/metacubex/mihomo/component/resolver" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" - - D "github.com/miekg/dns" ) type Dns struct { @@ -79,12 +76,12 @@ func (d *dnsPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } func (d *dnsPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - ctx, cancel := context.WithTimeout(d.ctx, time.Second*5) + ctx, cancel := context.WithTimeout(d.ctx, resolver.DefaultDnsRelayTimeout) defer cancel() - buf := pool.Get(2048) + buf := pool.Get(resolver.SafeDnsPacketSize) put := func() { _ = pool.Put(buf) } - buf, err = RelayDnsPacket(ctx, p, buf) + buf, err = resolver.RelayDnsPacket(ctx, p, buf) if err != nil { put() return 0, err @@ -110,7 +107,11 @@ func (d *dnsPacketConn) Close() error { } func (*dnsPacketConn) LocalAddr() net.Addr { - return net.UDPAddrFromAddrPort(netip.MustParseAddrPort("127.0.0.1:53")) + return &net.UDPAddr{ + IP: net.IPv4(127, 0, 0, 1), + Port: 53, + Zone: "", + } } func (*dnsPacketConn) SetDeadline(t time.Time) error { @@ -139,22 +140,3 @@ func NewDnsWithOption(option DnsOption) *Dns { }, } } - -// copied from listener/sing_mux/dns.go -func RelayDnsPacket(ctx context.Context, payload []byte, target []byte) ([]byte, error) { - msg := &D.Msg{} - if err := msg.Unpack(payload); err != nil { - return nil, err - } - - r, err := resolver.ServeMsg(ctx, msg) - if err != nil { - m := new(D.Msg) - m.SetRcode(msg, D.RcodeServerFailure) - return m.PackBuffer(target) - } - - r.SetRcode(msg, r.Rcode) - r.Compress = true - return r.PackBuffer(target) -} diff --git a/component/resolver/relay.go b/component/resolver/relay.go new file mode 100644 index 00000000..3bc54445 --- /dev/null +++ b/component/resolver/relay.go @@ -0,0 +1,88 @@ +package resolver + +import ( + "context" + "encoding/binary" + "io" + "net" + "time" + + "github.com/metacubex/mihomo/common/pool" + + D "github.com/miekg/dns" +) + +const DefaultDnsReadTimeout = time.Second * 10 +const DefaultDnsRelayTimeout = time.Second * 5 + +const SafeDnsPacketSize = 2 * 1024 // safe size which is 1232 from https://dnsflagday.net/2020/, so 2048 is enough + +func RelayDnsConn(ctx context.Context, conn net.Conn) error { + buff := pool.Get(pool.UDPBufferSize) + defer func() { + _ = pool.Put(buff) + _ = conn.Close() + }() + for { + if conn.SetReadDeadline(time.Now().Add(DefaultDnsReadTimeout)) != nil { + break + } + + length := uint16(0) + if err := binary.Read(conn, binary.BigEndian, &length); err != nil { + break + } + + if int(length) > len(buff) { + break + } + + n, err := io.ReadFull(conn, buff[:length]) + if err != nil { + break + } + + err = func() error { + ctx, cancel := context.WithTimeout(ctx, DefaultDnsRelayTimeout) + defer cancel() + inData := buff[:n] + msg, err := RelayDnsPacket(ctx, inData, buff) + if err != nil { + return err + } + + err = binary.Write(conn, binary.BigEndian, uint16(len(msg))) + if err != nil { + return err + } + + _, err = conn.Write(msg) + if err != nil { + return err + } + return nil + }() + if err != nil { + return err + } + } + return nil +} + +func RelayDnsPacket(ctx context.Context, payload []byte, target []byte) ([]byte, error) { + msg := &D.Msg{} + if err := msg.Unpack(payload); err != nil { + return nil, err + } + + r, err := ServeMsg(ctx, msg) + if err != nil { + m := new(D.Msg) + m.SetRcode(msg, D.RcodeServerFailure) + return m.PackBuffer(target) + } + + r.SetRcode(msg, r.Rcode) + r.Compress = true + return r.PackBuffer(target) +} diff --git a/listener/sing_tun/dns.go b/listener/sing_tun/dns.go index 056c9169..86237daa 100644 --- a/listener/sing_tun/dns.go +++ b/listener/sing_tun/dns.go @@ -2,29 +2,21 @@ package sing_tun import ( "context" - "encoding/binary" - "io" "net" "net/netip" "sync" "time" - "github.com/metacubex/mihomo/common/pool" "github.com/metacubex/mihomo/component/resolver" "github.com/metacubex/mihomo/listener/sing" "github.com/metacubex/mihomo/log" - D "github.com/miekg/dns" - "github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/bufio" M "github.com/sagernet/sing/common/metadata" "github.com/sagernet/sing/common/network" ) -const DefaultDnsReadTimeout = time.Second * 10 -const DefaultDnsRelayTimeout = time.Second * 5 - type ListenerHandler struct { *sing.ListenerHandler DnsAdds []netip.AddrPort @@ -45,61 +37,11 @@ func (h *ListenerHandler) ShouldHijackDns(targetAddr netip.AddrPort) bool { func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { if h.ShouldHijackDns(metadata.Destination.AddrPort()) { log.Debugln("[DNS] hijack tcp:%s", metadata.Destination.String()) - buff := pool.Get(pool.UDPBufferSize) - defer func() { - _ = pool.Put(buff) - _ = conn.Close() - }() - for { - if conn.SetReadDeadline(time.Now().Add(DefaultDnsReadTimeout)) != nil { - break - } - - length := uint16(0) - if err := binary.Read(conn, binary.BigEndian, &length); err != nil { - break - } - - if int(length) > len(buff) { - break - } - - n, err := io.ReadFull(conn, buff[:length]) - if err != nil { - break - } - - err = func() error { - ctx, cancel := context.WithTimeout(ctx, DefaultDnsRelayTimeout) - defer cancel() - inData := buff[:n] - msg, err := RelayDnsPacket(ctx, inData, buff) - if err != nil { - return err - } - - err = binary.Write(conn, binary.BigEndian, uint16(len(msg))) - if err != nil { - return err - } - - _, err = conn.Write(msg) - if err != nil { - return err - } - return nil - }() - if err != nil { - return err - } - } - return nil + return resolver.RelayDnsConn(ctx, conn) } return h.ListenerHandler.NewConnection(ctx, conn, metadata) } -const SafeDnsPacketSize = 2 * 1024 // safe size which is 1232 from https://dnsflagday.net/2020/, so 2048 is enough - func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error { if h.ShouldHijackDns(metadata.Destination.AddrPort()) { log.Debugln("[DNS] hijack udp:%s from %s", metadata.Destination.String(), metadata.Source.String()) @@ -114,7 +56,7 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network. rwOptions := network.ReadWaitOptions{ FrontHeadroom: network.CalculateFrontHeadroom(conn), RearHeadroom: network.CalculateRearHeadroom(conn), - MTU: SafeDnsPacketSize, + MTU: resolver.SafeDnsPacketSize, } readWaiter, isReadWaiter := bufio.CreatePacketReadWaiter(conn) if isReadWaiter { @@ -126,7 +68,7 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network. dest M.Socksaddr err error ) - _ = conn.SetReadDeadline(time.Now().Add(DefaultDnsReadTimeout)) + _ = conn.SetReadDeadline(time.Now().Add(resolver.DefaultDnsReadTimeout)) readBuff = nil // clear last loop status, avoid repeat release if isReadWaiter { readBuff, dest, err = readWaiter.WaitReadPacket() @@ -147,15 +89,15 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network. return err } go func() { - ctx, cancel := context.WithTimeout(ctx, DefaultDnsRelayTimeout) + ctx, cancel := context.WithTimeout(ctx, resolver.DefaultDnsRelayTimeout) defer cancel() inData := readBuff.Bytes() writeBuff := readBuff writeBuff.Resize(writeBuff.Start(), 0) - if len(writeBuff.FreeBytes()) < SafeDnsPacketSize { // only create a new buffer when space don't enough + if len(writeBuff.FreeBytes()) < resolver.SafeDnsPacketSize { // only create a new buffer when space don't enough writeBuff = rwOptions.NewPacketBuffer() } - msg, err := RelayDnsPacket(ctx, inData, writeBuff.FreeBytes()) + msg, err := resolver.RelayDnsPacket(ctx, inData, writeBuff.FreeBytes()) if writeBuff != readBuff { readBuff.Release() } @@ -182,21 +124,3 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network. } return h.ListenerHandler.NewPacketConnection(ctx, conn, metadata) } - -func RelayDnsPacket(ctx context.Context, payload []byte, target []byte) ([]byte, error) { - msg := &D.Msg{} - if err := msg.Unpack(payload); err != nil { - return nil, err - } - - r, err := resolver.ServeMsg(ctx, msg) - if err != nil { - m := new(D.Msg) - m.SetRcode(msg, D.RcodeServerFailure) - return m.PackBuffer(target) - } - - r.SetRcode(msg, r.Rcode) - r.Compress = true - return r.PackBuffer(target) -} From 974332c0ccd64fde54e7714c093b2d40b6b982ef Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 5 Mar 2024 10:57:25 +0800 Subject: [PATCH 30/65] chore: rebuild sync.Once visit code --- common/net/earlyconn.go | 6 ++---- common/once/once_go120.go | 26 ++++++++++++++++++++++++++ common/once/once_go122.go | 26 ++++++++++++++++++++++++++ component/mmdb/mmdb.go | 3 ++- 4 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 common/once/once_go120.go create mode 100644 common/once/once_go122.go diff --git a/common/net/earlyconn.go b/common/net/earlyconn.go index c9a42819..82d392ee 100644 --- a/common/net/earlyconn.go +++ b/common/net/earlyconn.go @@ -3,10 +3,9 @@ package net import ( "net" "sync" - "sync/atomic" - "unsafe" "github.com/metacubex/mihomo/common/buf" + "github.com/metacubex/mihomo/common/once" ) type earlyConn struct { @@ -44,8 +43,7 @@ func (conn *earlyConn) Upstream() any { } func (conn *earlyConn) Success() bool { - // atomic visit sync.Once.done - return atomic.LoadUint32((*uint32)(unsafe.Pointer(&conn.resOnce))) == 1 && conn.resErr == nil + return once.Done(&conn.resOnce) && conn.resErr == nil } func (conn *earlyConn) ReaderReplaceable() bool { diff --git a/common/once/once_go120.go b/common/once/once_go120.go new file mode 100644 index 00000000..51578a2d --- /dev/null +++ b/common/once/once_go120.go @@ -0,0 +1,26 @@ +//go:build !go1.22 + +package once + +import ( + "sync" + "sync/atomic" + "unsafe" +) + +type Once struct { + done uint32 + m sync.Mutex +} + +func Done(once *sync.Once) bool { + // atomic visit sync.Once.done + return atomic.LoadUint32((*uint32)(unsafe.Pointer(once))) == 1 +} + +func Reset(once *sync.Once) { + o := (*Once)(unsafe.Pointer(once)) + o.m.Lock() + defer o.m.Unlock() + atomic.StoreUint32(&o.done, 0) +} diff --git a/common/once/once_go122.go b/common/once/once_go122.go new file mode 100644 index 00000000..5ff8461d --- /dev/null +++ b/common/once/once_go122.go @@ -0,0 +1,26 @@ +//go:build go1.22 + +package once + +import ( + "sync" + "sync/atomic" + "unsafe" +) + +type Once struct { + done atomic.Uint32 + m sync.Mutex +} + +func Done(once *sync.Once) bool { + // atomic visit sync.Once.done + return (*atomic.Uint32)(unsafe.Pointer(once)).Load() == 1 +} + +func Reset(once *sync.Once) { + o := (*Once)(unsafe.Pointer(once)) + o.m.Lock() + defer o.m.Unlock() + o.done.Store(0) +} diff --git a/component/mmdb/mmdb.go b/component/mmdb/mmdb.go index 24edb5e5..66b632be 100644 --- a/component/mmdb/mmdb.go +++ b/component/mmdb/mmdb.go @@ -8,6 +8,7 @@ import ( "sync" "time" + mihomoOnce "github.com/metacubex/mihomo/common/once" mihomoHttp "github.com/metacubex/mihomo/component/http" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" @@ -96,5 +97,5 @@ func DownloadMMDB(path string) (err error) { } func Reload() { - once = sync.Once{} + mihomoOnce.Reset(&once) } From 823f59b5c79af7c7748664c11a825c716f2b00db Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Thu, 7 Mar 2024 00:52:20 +0800 Subject: [PATCH 31/65] chore: Add `dns-redirect` options to `iptables` --- config/config.go | 2 + hub/executor/executor.go | 23 ++++++----- listener/tproxy/tproxy_iptables.go | 61 +++++++++++++++++++----------- 3 files changed, 54 insertions(+), 32 deletions(-) diff --git a/config/config.go b/config/config.go index c5f4bb77..d4b9ad89 100644 --- a/config/config.go +++ b/config/config.go @@ -152,6 +152,7 @@ type IPTables struct { Enable bool `yaml:"enable" json:"enable"` InboundInterface string `yaml:"inbound-interface" json:"inbound-interface"` Bypass []string `yaml:"bypass" json:"bypass"` + DnsRedirect bool `yaml:"dns-redirect" json:"dns-redirect"` } type Sniffer struct { @@ -440,6 +441,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { Enable: false, InboundInterface: "lo", Bypass: []string{}, + DnsRedirect: true, }, NTP: RawNTP{ Enable: false, diff --git a/hub/executor/executor.go b/hub/executor/executor.go index e4a31a79..14e826d7 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -478,6 +478,9 @@ func updateIPTables(cfg *config.Config) { bypass = iptables.Bypass tProxyPort = cfg.General.TProxyPort dnsCfg = cfg.DNS + DnsRedirect = iptables.DnsRedirect + + dnsPort netip.AddrPort ) if tProxyPort == 0 { @@ -485,15 +488,17 @@ func updateIPTables(cfg *config.Config) { return } - if !dnsCfg.Enable { - err = fmt.Errorf("DNS server must be enable") - return - } + if DnsRedirect { + if !dnsCfg.Enable { + err = fmt.Errorf("DNS server must be enable") + return + } - dnsPort, err := netip.ParseAddrPort(dnsCfg.Listen) - if err != nil { - err = fmt.Errorf("DNS server must be correct") - return + dnsPort, err = netip.ParseAddrPort(dnsCfg.Listen) + if err != nil { + err = fmt.Errorf("DNS server must be correct") + return + } } if iptables.InboundInterface != "" { @@ -504,7 +509,7 @@ func updateIPTables(cfg *config.Config) { dialer.DefaultRoutingMark.Store(2158) } - err = tproxy.SetTProxyIPTables(inboundInterface, bypass, uint16(tProxyPort), dnsPort.Port()) + err = tproxy.SetTProxyIPTables(inboundInterface, bypass, uint16(tProxyPort), DnsRedirect, dnsPort.Port()) if err != nil { return } diff --git a/listener/tproxy/tproxy_iptables.go b/listener/tproxy/tproxy_iptables.go index 5ddd7b4c..6c6e2cc8 100644 --- a/listener/tproxy/tproxy_iptables.go +++ b/listener/tproxy/tproxy_iptables.go @@ -15,6 +15,7 @@ var ( dnsPort uint16 tProxyPort uint16 interfaceName string + DnsRedirect bool ) const ( @@ -22,7 +23,7 @@ const ( PROXY_ROUTE_TABLE = "0x2d0" ) -func SetTProxyIPTables(ifname string, bypass []string, tport uint16, dport uint16) error { +func SetTProxyIPTables(ifname string, bypass []string, tport uint16, dnsredir bool, dport uint16) error { if _, err := cmd.ExecCmd("iptables -V"); err != nil { return fmt.Errorf("current operations system [%s] are not support iptables or command iptables does not exist", runtime.GOOS) } @@ -33,6 +34,7 @@ func SetTProxyIPTables(ifname string, bypass []string, tport uint16, dport uint1 interfaceName = ifname tProxyPort = tport + DnsRedirect = dnsredir dnsPort = dport // add route @@ -58,8 +60,10 @@ func SetTProxyIPTables(ifname string, bypass []string, tport uint16, dport uint1 execCmd("iptables -t mangle -N mihomo_prerouting") execCmd("iptables -t mangle -F mihomo_prerouting") execCmd("iptables -t mangle -A mihomo_prerouting -s 172.17.0.0/16 -j RETURN") - execCmd("iptables -t mangle -A mihomo_prerouting -p udp --dport 53 -j ACCEPT") - execCmd("iptables -t mangle -A mihomo_prerouting -p tcp --dport 53 -j ACCEPT") + if DnsRedirect { + execCmd("iptables -t mangle -A mihomo_prerouting -p udp --dport 53 -j ACCEPT") + execCmd("iptables -t mangle -A mihomo_prerouting -p tcp --dport 53 -j ACCEPT") + } execCmd("iptables -t mangle -A mihomo_prerouting -m addrtype --dst-type LOCAL -j RETURN") addLocalnetworkToChain("mihomo_prerouting", bypass) execCmd("iptables -t mangle -A mihomo_prerouting -p tcp -m socket -j mihomo_divert") @@ -68,8 +72,10 @@ func SetTProxyIPTables(ifname string, bypass []string, tport uint16, dport uint1 execCmd(fmt.Sprintf("iptables -t mangle -A mihomo_prerouting -p udp -j TPROXY --on-port %d --tproxy-mark %s/%s", tProxyPort, PROXY_FWMARK, PROXY_FWMARK)) execCmd("iptables -t mangle -A PREROUTING -j mihomo_prerouting") - execCmd(fmt.Sprintf("iptables -t nat -I PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p tcp --dport 53 -j REDIRECT --to %d", dnsPort)) - execCmd(fmt.Sprintf("iptables -t nat -I PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p udp --dport 53 -j REDIRECT --to %d", dnsPort)) + if DnsRedirect { + execCmd(fmt.Sprintf("iptables -t nat -I PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p tcp --dport 53 -j REDIRECT --to %d", dnsPort)) + execCmd(fmt.Sprintf("iptables -t nat -I PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p udp --dport 53 -j REDIRECT --to %d", dnsPort)) + } // set post routing if interfaceName != "lo" { @@ -80,8 +86,10 @@ func SetTProxyIPTables(ifname string, bypass []string, tport uint16, dport uint1 execCmd("iptables -t mangle -N mihomo_output") execCmd("iptables -t mangle -F mihomo_output") execCmd(fmt.Sprintf("iptables -t mangle -A mihomo_output -m mark --mark %#x -j RETURN", dialer.DefaultRoutingMark.Load())) - execCmd("iptables -t mangle -A mihomo_output -p udp -m multiport --dports 53,123,137 -j ACCEPT") - execCmd("iptables -t mangle -A mihomo_output -p tcp --dport 53 -j ACCEPT") + if DnsRedirect { + execCmd("iptables -t mangle -A mihomo_output -p udp -m multiport --dports 53,123,137 -j ACCEPT") + execCmd("iptables -t mangle -A mihomo_output -p tcp --dport 53 -j ACCEPT") + } execCmd("iptables -t mangle -A mihomo_output -m addrtype --dst-type LOCAL -j RETURN") execCmd("iptables -t mangle -A mihomo_output -m addrtype --dst-type BROADCAST -j RETURN") addLocalnetworkToChain("mihomo_output", bypass) @@ -90,20 +98,22 @@ func SetTProxyIPTables(ifname string, bypass []string, tport uint16, dport uint1 execCmd(fmt.Sprintf("iptables -t mangle -I OUTPUT -o %s -j mihomo_output", interfaceName)) // set dns output - execCmd("iptables -t nat -N mihomo_dns_output") - execCmd("iptables -t nat -F mihomo_dns_output") - execCmd(fmt.Sprintf("iptables -t nat -A mihomo_dns_output -m mark --mark %#x -j RETURN", dialer.DefaultRoutingMark.Load())) - execCmd("iptables -t nat -A mihomo_dns_output -s 172.17.0.0/16 -j RETURN") - execCmd(fmt.Sprintf("iptables -t nat -A mihomo_dns_output -p udp -j REDIRECT --to-ports %d", dnsPort)) - execCmd(fmt.Sprintf("iptables -t nat -A mihomo_dns_output -p tcp -j REDIRECT --to-ports %d", dnsPort)) - execCmd("iptables -t nat -I OUTPUT -p tcp --dport 53 -j mihomo_dns_output") - execCmd("iptables -t nat -I OUTPUT -p udp --dport 53 -j mihomo_dns_output") + if DnsRedirect { + execCmd("iptables -t nat -N mihomo_dns_output") + execCmd("iptables -t nat -F mihomo_dns_output") + execCmd(fmt.Sprintf("iptables -t nat -A mihomo_dns_output -m mark --mark %#x -j RETURN", dialer.DefaultRoutingMark.Load())) + execCmd("iptables -t nat -A mihomo_dns_output -s 172.17.0.0/16 -j RETURN") + execCmd(fmt.Sprintf("iptables -t nat -A mihomo_dns_output -p udp -j REDIRECT --to-ports %d", dnsPort)) + execCmd(fmt.Sprintf("iptables -t nat -A mihomo_dns_output -p tcp -j REDIRECT --to-ports %d", dnsPort)) + execCmd("iptables -t nat -I OUTPUT -p tcp --dport 53 -j mihomo_dns_output") + execCmd("iptables -t nat -I OUTPUT -p udp --dport 53 -j mihomo_dns_output") + } return nil } func CleanupTProxyIPTables() { - if runtime.GOOS != "linux" || interfaceName == "" || tProxyPort == 0 || dnsPort == 0 { + if runtime.GOOS != "linux" || interfaceName == "" || tProxyPort == 0 { return } @@ -130,8 +140,10 @@ func CleanupTProxyIPTables() { } // clean PREROUTING - execCmd(fmt.Sprintf("iptables -t nat -D PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p tcp --dport 53 -j REDIRECT --to %d", dnsPort)) - execCmd(fmt.Sprintf("iptables -t nat -D PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p udp --dport 53 -j REDIRECT --to %d", dnsPort)) + if DnsRedirect { + execCmd(fmt.Sprintf("iptables -t nat -D PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p tcp --dport 53 -j REDIRECT --to %d", dnsPort)) + execCmd(fmt.Sprintf("iptables -t nat -D PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p udp --dport 53 -j REDIRECT --to %d", dnsPort)) + } execCmd("iptables -t mangle -D PREROUTING -j mihomo_prerouting") // clean POSTROUTING @@ -141,8 +153,10 @@ func CleanupTProxyIPTables() { // clean OUTPUT execCmd(fmt.Sprintf("iptables -t mangle -D OUTPUT -o %s -j mihomo_output", interfaceName)) - execCmd("iptables -t nat -D OUTPUT -p tcp --dport 53 -j mihomo_dns_output") - execCmd("iptables -t nat -D OUTPUT -p udp --dport 53 -j mihomo_dns_output") + if DnsRedirect { + execCmd("iptables -t nat -D OUTPUT -p tcp --dport 53 -j mihomo_dns_output") + execCmd("iptables -t nat -D OUTPUT -p udp --dport 53 -j mihomo_dns_output") + } // clean chain execCmd("iptables -t mangle -F mihomo_prerouting") @@ -151,9 +165,10 @@ func CleanupTProxyIPTables() { execCmd("iptables -t mangle -X mihomo_divert") execCmd("iptables -t mangle -F mihomo_output") execCmd("iptables -t mangle -X mihomo_output") - execCmd("iptables -t nat -F mihomo_dns_output") - execCmd("iptables -t nat -X mihomo_dns_output") - + if DnsRedirect { + execCmd("iptables -t nat -F mihomo_dns_output") + execCmd("iptables -t nat -X mihomo_dns_output") + } interfaceName = "" tProxyPort = 0 dnsPort = 0 From 04886761a28e2d0a06d9e6f51d3c31bfb4855d28 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Thu, 7 Mar 2024 03:35:11 +0800 Subject: [PATCH 32/65] chore: Add `max-failed-times` --- adapter/outboundgroup/fallback.go | 2 ++ adapter/outboundgroup/groupbase.go | 37 ++++++++++++++-------------- adapter/outboundgroup/loadbalance.go | 2 ++ adapter/outboundgroup/parser.go | 1 + adapter/outboundgroup/relay.go | 2 ++ adapter/outboundgroup/selector.go | 2 ++ adapter/outboundgroup/urltest.go | 2 ++ 7 files changed, 30 insertions(+), 18 deletions(-) diff --git a/adapter/outboundgroup/fallback.go b/adapter/outboundgroup/fallback.go index 4c8a2247..9387f7de 100644 --- a/adapter/outboundgroup/fallback.go +++ b/adapter/outboundgroup/fallback.go @@ -164,6 +164,8 @@ func NewFallback(option *GroupCommonOption, providers []provider.ProxyProvider) option.Filter, option.ExcludeFilter, option.ExcludeType, + option.TestTimeout, + option.MaxFailedTimes, providers, }), disableUDP: option.DisableUDP, diff --git a/adapter/outboundgroup/groupbase.go b/adapter/outboundgroup/groupbase.go index 0ea3685b..b39ee3a6 100644 --- a/adapter/outboundgroup/groupbase.go +++ b/adapter/outboundgroup/groupbase.go @@ -31,14 +31,18 @@ type GroupBase struct { failedTesting atomic.Bool proxies [][]C.Proxy versions []atomic.Uint32 + TestTimeout int + maxFailedTimes int } type GroupBaseOption struct { outbound.BaseOption - filter string - excludeFilter string - excludeType string - providers []provider.ProxyProvider + filter string + excludeFilter string + excludeType string + TestTimeout int + maxFailedTimes int + providers []provider.ProxyProvider } func NewGroupBase(opt GroupBaseOption) *GroupBase { @@ -66,6 +70,15 @@ func NewGroupBase(opt GroupBaseOption) *GroupBase { excludeTypeArray: excludeTypeArray, providers: opt.providers, failedTesting: atomic.NewBool(false), + TestTimeout: opt.TestTimeout, + maxFailedTimes: opt.maxFailedTimes, + } + + if gb.TestTimeout == 0 { + gb.TestTimeout = 5000 + } + if gb.maxFailedTimes == 0 { + gb.maxFailedTimes = 5 } gb.proxies = make([][]C.Proxy, len(opt.providers)) @@ -240,13 +253,13 @@ func (gb *GroupBase) onDialFailed(adapterType C.AdapterType, err error) { log.Debugln("ProxyGroup: %s first failed", gb.Name()) gb.failedTime = time.Now() } else { - if time.Since(gb.failedTime) > gb.failedTimeoutInterval() { + if time.Since(gb.failedTime) > time.Duration(gb.TestTimeout)*time.Millisecond { gb.failedTimes = 0 return } log.Debugln("ProxyGroup: %s failed count: %d", gb.Name(), gb.failedTimes) - if gb.failedTimes >= gb.maxFailedTimes() { + if gb.failedTimes >= gb.maxFailedTimes { log.Warnln("because %s failed multiple times, active health check", gb.Name()) gb.healthCheck() } @@ -275,20 +288,8 @@ func (gb *GroupBase) healthCheck() { gb.failedTimes = 0 } -func (gb *GroupBase) failedIntervalTime() int64 { - return 5 * time.Second.Milliseconds() -} - func (gb *GroupBase) onDialSuccess() { if !gb.failedTesting.Load() { gb.failedTimes = 0 } } - -func (gb *GroupBase) maxFailedTimes() int { - return 5 -} - -func (gb *GroupBase) failedTimeoutInterval() time.Duration { - return 5 * time.Second -} diff --git a/adapter/outboundgroup/loadbalance.go b/adapter/outboundgroup/loadbalance.go index 976a2e89..4cb0db00 100644 --- a/adapter/outboundgroup/loadbalance.go +++ b/adapter/outboundgroup/loadbalance.go @@ -266,6 +266,8 @@ func NewLoadBalance(option *GroupCommonOption, providers []provider.ProxyProvide option.Filter, option.ExcludeFilter, option.ExcludeType, + option.TestTimeout, + option.MaxFailedTimes, providers, }), strategyFn: strategyFn, diff --git a/adapter/outboundgroup/parser.go b/adapter/outboundgroup/parser.go index 96b23eb2..74947587 100644 --- a/adapter/outboundgroup/parser.go +++ b/adapter/outboundgroup/parser.go @@ -29,6 +29,7 @@ type GroupCommonOption struct { URL string `group:"url,omitempty"` Interval int `group:"interval,omitempty"` TestTimeout int `group:"timeout,omitempty"` + MaxFailedTimes int `group:"max-failed-times,omitempty"` Lazy bool `group:"lazy,omitempty"` DisableUDP bool `group:"disable-udp,omitempty"` Filter string `group:"filter,omitempty"` diff --git a/adapter/outboundgroup/relay.go b/adapter/outboundgroup/relay.go index 6a8e8bb1..07fbcd95 100644 --- a/adapter/outboundgroup/relay.go +++ b/adapter/outboundgroup/relay.go @@ -160,6 +160,8 @@ func NewRelay(option *GroupCommonOption, providers []provider.ProxyProvider) *Re "", "", "", + 5000, + 5, providers, }), Hidden: option.Hidden, diff --git a/adapter/outboundgroup/selector.go b/adapter/outboundgroup/selector.go index 3ac740f4..20eca70f 100644 --- a/adapter/outboundgroup/selector.go +++ b/adapter/outboundgroup/selector.go @@ -114,6 +114,8 @@ func NewSelector(option *GroupCommonOption, providers []provider.ProxyProvider) option.Filter, option.ExcludeFilter, option.ExcludeType, + option.TestTimeout, + option.MaxFailedTimes, providers, }), selected: "COMPATIBLE", diff --git a/adapter/outboundgroup/urltest.go b/adapter/outboundgroup/urltest.go index 8439772c..5da44f38 100644 --- a/adapter/outboundgroup/urltest.go +++ b/adapter/outboundgroup/urltest.go @@ -235,6 +235,8 @@ func NewURLTest(option *GroupCommonOption, providers []provider.ProxyProvider, o option.Filter, option.ExcludeFilter, option.ExcludeType, + option.TestTimeout, + option.MaxFailedTimes, providers, }), fastSingle: singledo.NewSingle[C.Proxy](time.Second * 10), From fad1a0837803ed7b959bcf92830b249c650d888d Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 7 Mar 2024 13:12:40 +0800 Subject: [PATCH 33/65] chore: dns outbound support tcp --- adapter/outbound/dns.go | 55 +++++++++++++++++++++----------- common/net/deadline/conn.go | 5 +++ common/net/deadline/pipe_sing.go | 5 +++ common/net/sing.go | 6 ++++ component/resolver/relay.go | 6 ++-- listener/sing_tun/dns.go | 2 +- 6 files changed, 56 insertions(+), 23 deletions(-) diff --git a/adapter/outbound/dns.go b/adapter/outbound/dns.go index 405392a1..21a5b2b7 100644 --- a/adapter/outbound/dns.go +++ b/adapter/outbound/dns.go @@ -2,10 +2,10 @@ package outbound import ( "context" - "fmt" "net" "time" + N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/common/pool" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/resolver" @@ -24,7 +24,9 @@ type DnsOption struct { // DialContext implements C.ProxyAdapter func (d *Dns) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { - return nil, fmt.Errorf("dns outbound does not support tcp") + left, right := N.Pipe() + go resolver.RelayDnsConn(context.Background(), right, 0) + return NewConn(left, d), nil } // ListenPacketContext implements C.ProxyAdapter @@ -76,29 +78,44 @@ func (d *dnsPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { } func (d *dnsPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + select { + case <-d.ctx.Done(): + return 0, net.ErrClosed + default: + } + + if len(p) > resolver.SafeDnsPacketSize { + // wtf??? + return len(p), nil + } + ctx, cancel := context.WithTimeout(d.ctx, resolver.DefaultDnsRelayTimeout) defer cancel() buf := pool.Get(resolver.SafeDnsPacketSize) put := func() { _ = pool.Put(buf) } - buf, err = resolver.RelayDnsPacket(ctx, p, buf) - if err != nil { - put() - return 0, err - } + copy(buf, p) // avoid p be changed after WriteTo returned - packet := dnsPacket{ - data: buf, - put: put, - addr: addr, - } - select { - case d.response <- packet: - return len(p), nil - case <-d.ctx.Done(): - put() - return 0, net.ErrClosed - } + go func() { // don't block the WriteTo function + buf, err = resolver.RelayDnsPacket(ctx, buf[:len(p)], buf) + if err != nil { + put() + return + } + + packet := dnsPacket{ + data: buf, + put: put, + addr: addr, + } + select { + case d.response <- packet: + break + case <-d.ctx.Done(): + put() + } + }() + return len(p), nil } func (d *dnsPacketConn) Close() error { diff --git a/common/net/deadline/conn.go b/common/net/deadline/conn.go index e8446ce2..fdf9334f 100644 --- a/common/net/deadline/conn.go +++ b/common/net/deadline/conn.go @@ -26,6 +26,11 @@ type Conn struct { resultCh chan *connReadResult } +func IsConn(conn any) bool { + _, ok := conn.(*Conn) + return ok +} + func NewConn(conn net.Conn) *Conn { c := &Conn{ ExtendedConn: bufio.NewExtendedConn(conn), diff --git a/common/net/deadline/pipe_sing.go b/common/net/deadline/pipe_sing.go index 20721fad..0f6d378d 100644 --- a/common/net/deadline/pipe_sing.go +++ b/common/net/deadline/pipe_sing.go @@ -215,3 +215,8 @@ func (p *pipe) waitReadBuffer() (buffer *buf.Buffer, err error) { return nil, os.ErrDeadlineExceeded } } + +func IsPipe(conn any) bool { + _, ok := conn.(*pipe) + return ok +} diff --git a/common/net/sing.go b/common/net/sing.go index 3296ad5b..d726f440 100644 --- a/common/net/sing.go +++ b/common/net/sing.go @@ -23,6 +23,12 @@ type ExtendedReader = network.ExtendedReader var WriteBuffer = bufio.WriteBuffer func NewDeadlineConn(conn net.Conn) ExtendedConn { + if deadline.IsPipe(conn) || deadline.IsPipe(network.UnwrapReader(conn)) { + return NewExtendedConn(conn) // pipe always have correctly deadline implement + } + if deadline.IsConn(conn) || deadline.IsConn(network.UnwrapReader(conn)) { + return NewExtendedConn(conn) // was a *deadline.Conn + } return deadline.NewConn(conn) } diff --git a/component/resolver/relay.go b/component/resolver/relay.go index 3bc54445..27b25af1 100644 --- a/component/resolver/relay.go +++ b/component/resolver/relay.go @@ -17,15 +17,15 @@ const DefaultDnsRelayTimeout = time.Second * 5 const SafeDnsPacketSize = 2 * 1024 // safe size which is 1232 from https://dnsflagday.net/2020/, so 2048 is enough -func RelayDnsConn(ctx context.Context, conn net.Conn) error { +func RelayDnsConn(ctx context.Context, conn net.Conn, readTimeout time.Duration) error { buff := pool.Get(pool.UDPBufferSize) defer func() { _ = pool.Put(buff) _ = conn.Close() }() for { - if conn.SetReadDeadline(time.Now().Add(DefaultDnsReadTimeout)) != nil { - break + if readTimeout > 0 { + _ = conn.SetReadDeadline(time.Now().Add(readTimeout)) } length := uint16(0) diff --git a/listener/sing_tun/dns.go b/listener/sing_tun/dns.go index 86237daa..42926732 100644 --- a/listener/sing_tun/dns.go +++ b/listener/sing_tun/dns.go @@ -37,7 +37,7 @@ func (h *ListenerHandler) ShouldHijackDns(targetAddr netip.AddrPort) bool { func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { if h.ShouldHijackDns(metadata.Destination.AddrPort()) { log.Debugln("[DNS] hijack tcp:%s", metadata.Destination.String()) - return resolver.RelayDnsConn(ctx, conn) + return resolver.RelayDnsConn(ctx, conn, resolver.DefaultDnsReadTimeout) } return h.ListenerHandler.NewConnection(ctx, conn, metadata) } From 234a4bfc93bfaf27e8ac4b64d2d677c45786e6a1 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Thu, 7 Mar 2024 23:32:07 +0800 Subject: [PATCH 34/65] feat: add `DOMAIN-REGEX` rule --- constant/rule.go | 3 +++ rules/common/domain_regex.go | 42 ++++++++++++++++++++++++++++++++++++ rules/parser.go | 4 +++- 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 rules/common/domain_regex.go diff --git a/constant/rule.go b/constant/rule.go index 66fc18bb..9b038221 100644 --- a/constant/rule.go +++ b/constant/rule.go @@ -5,6 +5,7 @@ const ( Domain RuleType = iota DomainSuffix DomainKeyword + DomainRegex GEOSITE GEOIP IPCIDR @@ -40,6 +41,8 @@ func (rt RuleType) String() string { return "DomainSuffix" case DomainKeyword: return "DomainKeyword" + case DomainRegex: + return "DomainRegex" case GEOSITE: return "GeoSite" case GEOIP: diff --git a/rules/common/domain_regex.go b/rules/common/domain_regex.go new file mode 100644 index 00000000..f1eb87e6 --- /dev/null +++ b/rules/common/domain_regex.go @@ -0,0 +1,42 @@ +package common + +import ( + "regexp" + "strings" + + C "github.com/metacubex/mihomo/constant" +) + +type DomainRegex struct { + *Base + regex string + adapter string +} + +func (dr *DomainRegex) RuleType() C.RuleType { + return C.DomainRegex +} + +func (dr *DomainRegex) Match(metadata *C.Metadata) (bool, string) { + domain := metadata.RuleHost() + match, _ := regexp.MatchString(dr.regex, domain) + return match, dr.adapter +} + +func (dr *DomainRegex) Adapter() string { + return dr.adapter +} + +func (dr *DomainRegex) Payload() string { + return dr.regex +} + +func NewDomainRegex(regex string, adapter string) *DomainRegex { + return &DomainRegex{ + Base: &Base{}, + regex: strings.ToLower(regex), + adapter: adapter, + } +} + +//var _ C.Rule = (*DomainRegex)(nil) diff --git a/rules/parser.go b/rules/parser.go index 7a79b18b..23f78123 100644 --- a/rules/parser.go +++ b/rules/parser.go @@ -2,7 +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" @@ -17,6 +17,8 @@ func ParseRule(tp, payload, target string, params []string, subRules map[string] parsed = RC.NewDomainSuffix(payload, target) case "DOMAIN-KEYWORD": parsed = RC.NewDomainKeyword(payload, target) + case "DOMAIN-REGEX": + parsed = RC.NewDomainRegex(payload, target) case "GEOSITE": parsed, parseErr = RC.NewGEOSITE(payload, target) case "GEOIP": From cd9e9cd2c128f64261b8abc3999682afa9f91b1f Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Fri, 8 Mar 2024 01:39:43 +0800 Subject: [PATCH 35/65] fix: fix timezone for Android --- main.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/main.go b/main.go index 748fa2e3..4b2dff9c 100644 --- a/main.go +++ b/main.go @@ -4,6 +4,7 @@ import ( "flag" "fmt" "os" + "os/exec" "os/signal" "path/filepath" "runtime" @@ -48,6 +49,10 @@ func init() { } func main() { + if runtime.GOOS == "android" { + SetAndroidTZ() + } + _, _ = maxprocs.Set(maxprocs.Logger(func(string, ...any) {})) if version { fmt.Printf("Mihomo Meta %s %s %s with %s %s\n", @@ -176,3 +181,15 @@ func updateGeoDatabases() { executor.ApplyConfig(cfg, false) }() } + +func SetAndroidTZ() { + out, err := exec.Command("getprop", "persist.sys.timezone").Output() + if err != nil { + return + } + z, err := time.LoadLocation(strings.TrimSpace(string(out))) + if err != nil { + return + } + time.Local = z +} From 37b02b18f7aeadeaecbdfbd49ba3edbb63619962 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Fri, 8 Mar 2024 17:37:32 +0800 Subject: [PATCH 36/65] chore: Temporarily abandon pkg.tar.zst package building --- .github/workflows/build.yml | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 30abd89d..bad84cd1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -204,18 +204,18 @@ jobs: alien --to-rpm --scripts mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.deb mv mihomo*.rpm mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.rpm - - name: Convert DEB to PKG - if: ${{ matrix.jobs.goos == 'linux' && !contains(matrix.jobs.goarch, 'mips') && !contains(matrix.jobs.goarch, 'loong64') }} - run: | - docker pull archlinux - docker run --rm -v ./:/mnt archlinux bash -c " - pacman -Syu pkgfile base-devel --noconfirm - curl -L https://github.com/helixarch/debtap/raw/master/debtap > /usr/bin/debtap - chmod 755 /usr/bin/debtap - debtap -u - debtap -Q /mnt/mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.deb - " - mv mihomo*.pkg.tar.zst mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.pkg.tar.zst + # - name: Convert DEB to PKG + # if: ${{ matrix.jobs.goos == 'linux' && !contains(matrix.jobs.goarch, 'mips') && !contains(matrix.jobs.goarch, 'loong64') }} + # run: | + # docker pull archlinux + # docker run --rm -v ./:/mnt archlinux bash -c " + # pacman -Syu pkgfile base-devel --noconfirm + # curl -L https://github.com/helixarch/debtap/raw/master/debtap > /usr/bin/debtap + # chmod 755 /usr/bin/debtap + # debtap -u + # debtap -Q /mnt/mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.deb + # " + # mv mihomo*.pkg.tar.zst mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.pkg.tar.zst - name: Save version run: | @@ -230,7 +230,6 @@ jobs: mihomo*.gz mihomo*.deb mihomo*.rpm - mihomo*.pkg.tar.zst mihomo*.zip version.txt From 0bb5568de918d0c999293b00db6a33cae6202ab0 Mon Sep 17 00:00:00 2001 From: TreviD Date: Fri, 8 Mar 2024 17:38:27 +0800 Subject: [PATCH 37/65] feat: add ssh outbound (#1087) * feat: add ssh outbound * fix: Modify the way to get dstAddr --------- Co-authored-by: trevid --- adapter/outbound/ssh.go | 98 +++++++++++++++++++++++++++++++++++++++++ adapter/parser.go | 7 +++ constant/adapters.go | 4 +- 3 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 adapter/outbound/ssh.go diff --git a/adapter/outbound/ssh.go b/adapter/outbound/ssh.go new file mode 100644 index 00000000..140a9331 --- /dev/null +++ b/adapter/outbound/ssh.go @@ -0,0 +1,98 @@ +package outbound + +import ( + "context" + "net" + "os" + "runtime" + "strconv" + + CN "github.com/metacubex/mihomo/common/net" + "github.com/metacubex/mihomo/component/dialer" + C "github.com/metacubex/mihomo/constant" + "golang.org/x/crypto/ssh" +) + +type Ssh struct { + *Base + + option *SshOption + client *ssh.Client +} + +type SshOption struct { + BasicOption + Name string `proxy:"name"` + Server string `proxy:"server"` + Port int `proxy:"port"` + UserName string `proxy:"username"` + Password string `proxy:"password,omitempty"` + PrivateKey string `proxy:"privateKey,omitempty"` +} + +func (h *Ssh) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { + c, err := h.client.Dial("tcp", metadata.RemoteAddress()) + if err != nil { + return nil, err + } + return NewConn(CN.NewRefConn(c, h), h), nil +} + +func closeSsh(h *Ssh) { + if h.client != nil { + _ = h.client.Close() + } +} + +func NewSsh(option SshOption) (*Ssh, error) { + addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) + + config := ssh.ClientConfig{ + User: option.UserName, + HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { + return nil + }, + } + + if option.Password == "" { + + b, err := os.ReadFile(option.PrivateKey) + if err != nil { + return nil, err + } + pKey, err := ssh.ParsePrivateKey(b) + if err != nil { + return nil, err + } + + config.Auth = []ssh.AuthMethod{ + ssh.PublicKeys(pKey), + } + } else { + config.Auth = []ssh.AuthMethod{ + ssh.Password(option.Password), + } + } + + client, err := ssh.Dial("tcp", addr, &config) + if err != nil { + return nil, err + } + + outbound := &Ssh{ + Base: &Base{ + name: option.Name, + addr: addr, + tp: C.Ssh, + udp: true, + iface: option.Interface, + rmark: option.RoutingMark, + prefer: C.NewDNSPrefer(option.IPVersion), + }, + option: &option, + client: client, + } + runtime.SetFinalizer(outbound, closeSsh) + + return outbound, nil +} diff --git a/adapter/parser.go b/adapter/parser.go index fa94708d..c64ee13a 100644 --- a/adapter/parser.go +++ b/adapter/parser.go @@ -134,6 +134,13 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) { break } proxy = outbound.NewRejectWithOption(*rejectOption) + case "ssh": + sshOption := &outbound.SshOption{} + err = decoder.Decode(mapping, sshOption) + if err != nil { + break + } + proxy, err = outbound.NewSsh(*sshOption) default: return nil, fmt.Errorf("unsupport proxy type: %s", proxyType) } diff --git a/constant/adapters.go b/constant/adapters.go index 105a7904..cb213b3c 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -41,6 +41,7 @@ const ( Hysteria2 WireGuard Tuic + Ssh ) const ( @@ -222,7 +223,8 @@ func (at AdapterType) String() string { return "URLTest" case LoadBalance: return "LoadBalance" - + case Ssh: + return "Ssh" default: return "Unknown" } From 5702d28cda2ba33d13dc1c9085b0203f75198285 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 8 Mar 2024 19:27:41 +0800 Subject: [PATCH 38/65] chore: rebuild ssh outbound --- adapter/outbound/ssh.go | 103 ++++++++++++++++++++++++++++++++++------ 1 file changed, 89 insertions(+), 14 deletions(-) diff --git a/adapter/outbound/ssh.go b/adapter/outbound/ssh.go index 140a9331..a41a8132 100644 --- a/adapter/outbound/ssh.go +++ b/adapter/outbound/ssh.go @@ -6,10 +6,14 @@ import ( "os" "runtime" "strconv" + "sync" - CN "github.com/metacubex/mihomo/common/net" + N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/dialer" + "github.com/metacubex/mihomo/component/proxydialer" C "github.com/metacubex/mihomo/constant" + + "github.com/zhangyunhao116/fastrand" "golang.org/x/crypto/ssh" ) @@ -17,7 +21,7 @@ type Ssh struct { *Base option *SshOption - client *ssh.Client + client *sshClient // using a standalone struct to avoid its inner loop invalidate the Finalizer } type SshOption struct { @@ -30,18 +34,85 @@ type SshOption struct { PrivateKey string `proxy:"privateKey,omitempty"` } -func (h *Ssh) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { - c, err := h.client.Dial("tcp", metadata.RemoteAddress()) +func (s *Ssh) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { + var cDialer C.Dialer = dialer.NewDialer(s.Base.DialOptions(opts...)...) + if len(s.option.DialerProxy) > 0 { + cDialer, err = proxydialer.NewByName(s.option.DialerProxy, cDialer) + if err != nil { + return nil, err + } + } + client, err := s.client.connect(ctx, cDialer, s.addr) if err != nil { return nil, err } - return NewConn(CN.NewRefConn(c, h), h), nil + c, err := client.DialContext(ctx, "tcp", metadata.RemoteAddress()) + if err != nil { + return nil, err + } + + return NewConn(N.NewRefConn(c, s), s), nil } -func closeSsh(h *Ssh) { - if h.client != nil { - _ = h.client.Close() +type sshClient struct { + config *ssh.ClientConfig + client *ssh.Client + cMutex sync.Mutex +} + +func (s *sshClient) connect(ctx context.Context, cDialer C.Dialer, addr string) (client *ssh.Client, err error) { + s.cMutex.Lock() + defer s.cMutex.Unlock() + if s.client != nil { + return s.client, nil } + c, err := cDialer.DialContext(ctx, "tcp", addr) + if err != nil { + return nil, err + } + N.TCPKeepAlive(c) + + defer func(c net.Conn) { + safeConnClose(c, err) + }(c) + + if ctx.Done() != nil { + done := N.SetupContextForConn(ctx, c) + defer done(&err) + } + + clientConn, chans, reqs, err := ssh.NewClientConn(c, addr, s.config) + if err != nil { + return nil, err + } + client = ssh.NewClient(clientConn, chans, reqs) + + s.client = client + + go func() { + _ = client.Wait() // wait shutdown + _ = client.Close() + s.cMutex.Lock() + defer s.cMutex.Unlock() + if s.client == client { + s.client = nil + } + }() + + return client, nil +} + +func (s *sshClient) Close() error { + s.cMutex.Lock() + defer s.cMutex.Unlock() + if s.client != nil { + return s.client.Close() + } + return nil +} + +func closeSsh(s *Ssh) { + _ = s.client.Close() } func NewSsh(option SshOption) (*Ssh, error) { @@ -55,7 +126,6 @@ func NewSsh(option SshOption) (*Ssh, error) { } if option.Password == "" { - b, err := os.ReadFile(option.PrivateKey) if err != nil { return nil, err @@ -74,23 +144,28 @@ func NewSsh(option SshOption) (*Ssh, error) { } } - client, err := ssh.Dial("tcp", addr, &config) - if err != nil { - return nil, err + version := "SSH-2.0-OpenSSH_" + if fastrand.Intn(2) == 0 { + version += "7." + strconv.Itoa(fastrand.Intn(10)) + } else { + version += "8." + strconv.Itoa(fastrand.Intn(9)) } + config.ClientVersion = version outbound := &Ssh{ Base: &Base{ name: option.Name, addr: addr, tp: C.Ssh, - udp: true, + udp: false, iface: option.Interface, rmark: option.RoutingMark, prefer: C.NewDNSPrefer(option.IPVersion), }, option: &option, - client: client, + client: &sshClient{ + config: &config, + }, } runtime.SetFinalizer(outbound, closeSsh) From 90d0ef033be2b13dc052ac75c88e5d0949bff5bb Mon Sep 17 00:00:00 2001 From: H1JK Date: Fri, 8 Mar 2024 22:42:48 +0800 Subject: [PATCH 39/65] chore: Check regex rule expression when initializing --- rules/common/domain_regex.go | 18 ++++++++++-------- rules/parser.go | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/rules/common/domain_regex.go b/rules/common/domain_regex.go index f1eb87e6..3d961542 100644 --- a/rules/common/domain_regex.go +++ b/rules/common/domain_regex.go @@ -2,14 +2,13 @@ package common import ( "regexp" - "strings" C "github.com/metacubex/mihomo/constant" ) type DomainRegex struct { *Base - regex string + regex *regexp.Regexp adapter string } @@ -19,8 +18,7 @@ func (dr *DomainRegex) RuleType() C.RuleType { func (dr *DomainRegex) Match(metadata *C.Metadata) (bool, string) { domain := metadata.RuleHost() - match, _ := regexp.MatchString(dr.regex, domain) - return match, dr.adapter + return dr.regex.MatchString(domain), dr.adapter } func (dr *DomainRegex) Adapter() string { @@ -28,15 +26,19 @@ func (dr *DomainRegex) Adapter() string { } func (dr *DomainRegex) Payload() string { - return dr.regex + return dr.regex.String() } -func NewDomainRegex(regex string, adapter string) *DomainRegex { +func NewDomainRegex(regex string, adapter string) (*DomainRegex, error) { + r, err := regexp.Compile(regex) + if err != nil { + return nil, err + } return &DomainRegex{ Base: &Base{}, - regex: strings.ToLower(regex), + regex: r, adapter: adapter, - } + }, nil } //var _ C.Rule = (*DomainRegex)(nil) diff --git a/rules/parser.go b/rules/parser.go index 23f78123..f7df5f49 100644 --- a/rules/parser.go +++ b/rules/parser.go @@ -18,7 +18,7 @@ func ParseRule(tp, payload, target string, params []string, subRules map[string] case "DOMAIN-KEYWORD": parsed = RC.NewDomainKeyword(payload, target) case "DOMAIN-REGEX": - parsed = RC.NewDomainRegex(payload, target) + parsed, parseErr = RC.NewDomainRegex(payload, target) case "GEOSITE": parsed, parseErr = RC.NewGEOSITE(payload, target) case "GEOIP": From 7754b46dc4932a6e019b173713bfa5c80ad18f32 Mon Sep 17 00:00:00 2001 From: H1JK Date: Fri, 8 Mar 2024 22:45:10 +0800 Subject: [PATCH 40/65] fix: MaxMind MMDB code character case --- component/mmdb/reader.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/component/mmdb/reader.go b/component/mmdb/reader.go index 6247cd8a..787bdfe8 100644 --- a/component/mmdb/reader.go +++ b/component/mmdb/reader.go @@ -3,6 +3,7 @@ package mmdb import ( "fmt" "net" + "strings" "github.com/oschwald/maxminddb-golang" ) @@ -26,7 +27,7 @@ func (r Reader) LookupCode(ipAddress net.IP) []string { if country.Country.IsoCode == "" { return []string{} } - return []string{country.Country.IsoCode} + return []string{strings.ToLower(country.Country.IsoCode)} case typeSing: var code string From feedc9ec66b8afca3c76c82202cf9b7d69af3e75 Mon Sep 17 00:00:00 2001 From: keakon Date: Fri, 8 Mar 2024 22:38:41 +0800 Subject: [PATCH 41/65] feat: implement port hopping (#1064) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * implement port hopping using sing and sing-quic * 更新quic-go * 更新sing * Update go.sum --------- Co-authored-by: wwqgtxx --- adapter/outbound/hysteria2.go | 62 +++++++++++++++++++++++++++++++++++ go.mod | 4 +-- go.sum | 9 ++--- 3 files changed, 69 insertions(+), 6 deletions(-) diff --git a/adapter/outbound/hysteria2.go b/adapter/outbound/hysteria2.go index 47272ec8..e55237d6 100644 --- a/adapter/outbound/hysteria2.go +++ b/adapter/outbound/hysteria2.go @@ -5,15 +5,19 @@ import ( "crypto/tls" "errors" "fmt" + "math/rand" "net" "runtime" "strconv" + "strings" + "time" CN "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/proxydialer" C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/log" tuicCommon "github.com/metacubex/mihomo/transport/tuic/common" "github.com/metacubex/sing-quic/hysteria2" @@ -25,6 +29,9 @@ func init() { hysteria2.SetCongestionController = tuicCommon.SetCongestionController } +const minHopInterval = 5 +const defaultHopInterval = 30 + type Hysteria2 struct { *Base @@ -38,6 +45,8 @@ type Hysteria2Option struct { Name string `proxy:"name"` Server string `proxy:"server"` Port int `proxy:"port"` + Ports string `proxy:"ports,omitempty"` + HopInterval int `proxy:"hop-interval,omitempty"` Up string `proxy:"up,omitempty"` Down string `proxy:"down,omitempty"` Password string `proxy:"password,omitempty"` @@ -82,6 +91,41 @@ func closeHysteria2(h *Hysteria2) { } } +func parsePorts(portStr string) (ports []uint16) { + portStrs := strings.Split(portStr, ",") + for _, portStr := range portStrs { + if strings.Contains(portStr, "-") { + // Port range + portRange := strings.Split(portStr, "-") + if len(portRange) != 2 { + return nil + } + start, err := strconv.ParseUint(portRange[0], 10, 16) + if err != nil { + return nil + } + end, err := strconv.ParseUint(portRange[1], 10, 16) + if err != nil { + return nil + } + if start > end { + start, end = end, start + } + for i := start; i <= end; i++ { + ports = append(ports, uint16(i)) + } + } else { + // Single port + port, err := strconv.ParseUint(portStr, 10, 16) + if err != nil { + return nil + } + ports = append(ports, uint16(port)) + } + } + return ports +} + func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) var salamanderPassword string @@ -129,6 +173,7 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { clientOptions := hysteria2.ClientOptions{ Context: context.TODO(), Dialer: singDialer, + Logger: log.SingLogger, ServerAddress: M.ParseSocksaddrHostPort(option.Server, uint16(option.Port)), SendBPS: StringToBps(option.Up), ReceiveBPS: StringToBps(option.Down), @@ -140,6 +185,23 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { UdpMTU: option.UdpMTU, } + if option.Ports != "" { + ports := parsePorts(option.Ports) + if len(ports) > 0 { + for _, port := range ports { + clientOptions.ServerAddresses = append(clientOptions.ServerAddresses, M.ParseSocksaddrHostPort(option.Server, port)) + } + clientOptions.ServerAddress = clientOptions.ServerAddresses[rand.Intn(len(clientOptions.ServerAddresses))] + + if option.HopInterval == 0 { + option.HopInterval = defaultHopInterval + } else if option.HopInterval < minHopInterval { + option.HopInterval = minHopInterval + } + clientOptions.HopInterval = time.Duration(option.HopInterval) * time.Second + } + } + client, err := hysteria2.NewClient(clientOptions) if err != nil { return nil, err diff --git a/go.mod b/go.mod index 7c28d2bf..e37ebc8e 100644 --- a/go.mod +++ b/go.mod @@ -19,8 +19,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.41.1-0.20240120014142-a02f4a533d4a - github.com/metacubex/sing-quic v0.0.0-20240130040922-cbe613c88f20 + github.com/metacubex/quic-go v0.41.1-0.20240307164142-46c6f7cdf2d1 + github.com/metacubex/sing-quic v0.0.0-20240308143007-4dd80423c25a github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 github.com/metacubex/sing-tun v0.2.1-0.20240214100323-23e40bfb9067 diff --git a/go.sum b/go.sum index 957b30de..f51a7f17 100644 --- a/go.sum +++ b/go.sum @@ -104,12 +104,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-20240214095142-666a73bcf165 h1:QIQI4gEm+gTwVNdiAyF4EIz5cHm7kSlfDGFpYlAa5dg= github.com/metacubex/gvisor v0.0.0-20240214095142-666a73bcf165/go.mod h1:SKY70wiF1UTSoyuDZyKPMsUC6MsMxh8Y3ZNkIa6J3fU= -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/quic-go v0.41.1-0.20240307164142-46c6f7cdf2d1 h1:63zKmEWU4MB5MjUSCmeDhm3OzilF7ypXWPq0gAA2GE8= +github.com/metacubex/quic-go v0.41.1-0.20240307164142-46c6f7cdf2d1/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-20240130040922-cbe613c88f20 h1:wt7ydRxm9Pvw+un6KD97tjLJHMrkzp83HyiGkoz6e7k= -github.com/metacubex/sing-quic v0.0.0-20240130040922-cbe613c88f20/go.mod h1:bdHqEysJclB9BzIa5jcKKSZ1qua+YEPjR8fOzzE3vZU= +github.com/metacubex/sing-quic v0.0.0-20240308143007-4dd80423c25a h1:ATj0jL+cp7n+NT3T010cXK5KoVvAbeGhZFtUFHvq2BU= +github.com/metacubex/sing-quic v0.0.0-20240308143007-4dd80423c25a/go.mod h1:WyY0zYxv+o+18R/Ece+QFontlgXoobKbNqbtYn2zjz8= 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= @@ -255,6 +255,7 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= 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= From e0248faebd9819c90307bb26e7d3cf65e0fbce3b Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sat, 9 Mar 2024 18:40:19 +0800 Subject: [PATCH 42/65] feat: Experimental supports dialer IP4P address convert form https://github.com/heiher/natmap/wiki/faq --- component/dialer/dialer.go | 29 ++++++++++++++++++++++++++++- config/config.go | 1 + hub/executor/executor.go | 1 + 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 070d98b8..12e7c960 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -7,12 +7,14 @@ import ( "net" "net/netip" "os" + "strconv" "strings" "sync" "time" "github.com/metacubex/mihomo/component/resolver" "github.com/metacubex/mihomo/constant/features" + "github.com/metacubex/mihomo/log" ) const ( @@ -24,6 +26,7 @@ type dialFunc func(ctx context.Context, network string, ips []netip.Addr, port s var ( dialMux sync.Mutex + IP4PEnable bool actualSingleStackDialContext = serialSingleStackDialContext actualDualStackDialContext = serialDualStackDialContext tcpConcurrent = false @@ -128,7 +131,13 @@ func dialContext(ctx context.Context, network string, destination netip.Addr, po return dialContextHooked(ctx, network, destination, port) } - address := net.JoinHostPort(destination.String(), port) + var address string + if IP4PEnable { + NewDestination, NewPort := lookupIP4P(destination.String(), port) + address = net.JoinHostPort(NewDestination, NewPort) + } else { + address = net.JoinHostPort(destination.String(), port) + } netDialer := opt.netDialer switch netDialer.(type) { @@ -383,3 +392,21 @@ func NewDialer(options ...Option) Dialer { opt := applyOptions(options...) return Dialer{Opt: *opt} } + +func GetIP4PEnable(enableIP4PConvert bool) { + IP4PEnable = enableIP4PConvert +} + +// kanged from https://github.com/heiher/frp/blob/ip4p/client/ip4p.go + +func lookupIP4P(addr string, port string) (string, string) { + ip := net.ParseIP(addr) + if ip[0] == 0x20 && ip[1] == 0x01 && + ip[2] == 0x00 && ip[3] == 0x00 { + addr = net.IPv4(ip[12], ip[13], ip[14], ip[15]).String() + port = strconv.Itoa(int(ip[10])<<8 + int(ip[11])) + log.Debugln("Convert IP4P address %s to %s", ip, net.JoinHostPort(addr, port)) + return addr, port + } + return addr, port +} diff --git a/config/config.go b/config/config.go index d4b9ad89..8fec0bab 100644 --- a/config/config.go +++ b/config/config.go @@ -169,6 +169,7 @@ type Experimental struct { Fingerprints []string `yaml:"fingerprints"` QUICGoDisableGSO bool `yaml:"quic-go-disable-gso"` QUICGoDisableECN bool `yaml:"quic-go-disable-ecn"` + IP4PEnable bool `yaml:"dialer-ip4p-convert"` } // Config is mihomo config manager diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 14e826d7..c23eb9f3 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -197,6 +197,7 @@ func updateExperimental(c *config.Config) { if c.Experimental.QUICGoDisableECN { _ = os.Setenv("QUIC_GO_DISABLE_ECN", strconv.FormatBool(true)) } + dialer.GetIP4PEnable(c.Experimental.IP4PEnable) } func updateNTP(c *config.NTP) { From 77c10d90f3b13c26bc4a26a8258054b664773d0d Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sat, 9 Mar 2024 19:25:26 +0800 Subject: [PATCH 43/65] chore: Replace android timezone implementation kanged from https://github.com/SagerNet/sing-box/blob/dev-next/include/tz_android.go --- android_tz.go | 21 +++++++++++++++++++++ main.go | 17 ----------------- 2 files changed, 21 insertions(+), 17 deletions(-) create mode 100644 android_tz.go diff --git a/android_tz.go b/android_tz.go new file mode 100644 index 00000000..82fc38e3 --- /dev/null +++ b/android_tz.go @@ -0,0 +1,21 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// kanged from https://github.com/golang/mobile/blob/c713f31d574bb632a93f169b2cc99c9e753fef0e/app/android.go#L89 + +package main + +// #include +import "C" +import "time" + +func init() { + var currentT C.time_t + var currentTM C.struct_tm + C.time(¤tT) + C.localtime_r(¤tT, ¤tTM) + tzOffset := int(currentTM.tm_gmtoff) + tz := C.GoString(currentTM.tm_zone) + time.Local = time.FixedZone(tz, tzOffset) +} diff --git a/main.go b/main.go index 4b2dff9c..748fa2e3 100644 --- a/main.go +++ b/main.go @@ -4,7 +4,6 @@ import ( "flag" "fmt" "os" - "os/exec" "os/signal" "path/filepath" "runtime" @@ -49,10 +48,6 @@ func init() { } func main() { - if runtime.GOOS == "android" { - SetAndroidTZ() - } - _, _ = maxprocs.Set(maxprocs.Logger(func(string, ...any) {})) if version { fmt.Printf("Mihomo Meta %s %s %s with %s %s\n", @@ -181,15 +176,3 @@ func updateGeoDatabases() { executor.ApplyConfig(cfg, false) }() } - -func SetAndroidTZ() { - out, err := exec.Command("getprop", "persist.sys.timezone").Output() - if err != nil { - return - } - z, err := time.LoadLocation(strings.TrimSpace(string(out))) - if err != nil { - return - } - time.Local = z -} From f0ff6546e42273459751449b569d8183ebc780f7 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sun, 10 Mar 2024 20:38:30 +0800 Subject: [PATCH 44/65] chore: Correct android update name --- hub/updater/updater.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hub/updater/updater.go b/hub/updater/updater.go index 1967af42..02ff07ba 100644 --- a/hub/updater/updater.go +++ b/hub/updater/updater.go @@ -137,6 +137,8 @@ func prepare(exePath string) (err error) { if runtime.GOOS == "windows" { updateExeName = "mihomo" + "-" + runtime.GOOS + "-" + runtime.GOARCH + amd64Compatible + ".exe" + } else if runtime.GOOS == "android" && runtime.GOARCH == "arm64" { + updateExeName = "mihomo-android-arm64-v8" } else { updateExeName = "mihomo" + "-" + runtime.GOOS + "-" + runtime.GOARCH + amd64Compatible } @@ -440,7 +442,11 @@ func updateDownloadURL() { middle = fmt.Sprintf("-%s-%s%s-%s", runtime.GOOS, runtime.GOARCH, goarm, latestVersion) } else if runtime.GOARCH == "arm64" { //-linux-arm64-alpha-e552b54.gz - middle = fmt.Sprintf("-%s-%s-%s", runtime.GOOS, runtime.GOARCH, latestVersion) + if runtime.GOOS == "android" { + middle = fmt.Sprintf("-%s-%s-v8-%s", runtime.GOOS, runtime.GOARCH, latestVersion) + } else { + middle = fmt.Sprintf("-%s-%s-%s", runtime.GOOS, runtime.GOARCH, latestVersion) + } } else if isMIPS(runtime.GOARCH) && gomips != "" { middle = fmt.Sprintf("-%s-%s-%s-%s", runtime.GOOS, runtime.GOARCH, gomips, latestVersion) } else { From 7ad37ca0e30550038d8fd62ce662969194365215 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 10 Mar 2024 23:49:54 +0800 Subject: [PATCH 45/65] fix: hysteria2 server domain resolve --- adapter/outbound/hysteria2.go | 15 ++++++++++----- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/adapter/outbound/hysteria2.go b/adapter/outbound/hysteria2.go index e55237d6..5c817373 100644 --- a/adapter/outbound/hysteria2.go +++ b/adapter/outbound/hysteria2.go @@ -5,7 +5,6 @@ import ( "crypto/tls" "errors" "fmt" - "math/rand" "net" "runtime" "strconv" @@ -23,6 +22,7 @@ import ( "github.com/metacubex/sing-quic/hysteria2" M "github.com/sagernet/sing/common/metadata" + "github.com/zhangyunhao116/fastrand" ) func init() { @@ -174,7 +174,6 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { Context: context.TODO(), Dialer: singDialer, Logger: log.SingLogger, - ServerAddress: M.ParseSocksaddrHostPort(option.Server, uint16(option.Port)), SendBPS: StringToBps(option.Up), ReceiveBPS: StringToBps(option.Down), SalamanderPassword: salamanderPassword, @@ -183,15 +182,21 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { UDPDisabled: false, CWND: option.CWND, UdpMTU: option.UdpMTU, + ServerAddress: func(ctx context.Context) (*net.UDPAddr, error) { + return resolveUDPAddrWithPrefer(ctx, "udp", addr, C.NewDNSPrefer(option.IPVersion)) + }, } if option.Ports != "" { ports := parsePorts(option.Ports) if len(ports) > 0 { - for _, port := range ports { - clientOptions.ServerAddresses = append(clientOptions.ServerAddresses, M.ParseSocksaddrHostPort(option.Server, port)) + serverAddress := make([]string, len(ports)) + for i, port := range ports { + serverAddress[i] = net.JoinHostPort(option.Server, strconv.Itoa(int(port))) + } + clientOptions.ServerAddress = func(ctx context.Context) (*net.UDPAddr, error) { + return resolveUDPAddrWithPrefer(ctx, "udp", serverAddress[fastrand.Intn(len(serverAddress))], C.NewDNSPrefer(option.IPVersion)) } - clientOptions.ServerAddress = clientOptions.ServerAddresses[rand.Intn(len(clientOptions.ServerAddresses))] if option.HopInterval == 0 { option.HopInterval = defaultHopInterval diff --git a/go.mod b/go.mod index e37ebc8e..cdc2c8cb 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,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.20240307164142-46c6f7cdf2d1 - github.com/metacubex/sing-quic v0.0.0-20240308143007-4dd80423c25a + github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 github.com/metacubex/sing-tun v0.2.1-0.20240214100323-23e40bfb9067 diff --git a/go.sum b/go.sum index f51a7f17..fe671108 100644 --- a/go.sum +++ b/go.sum @@ -108,8 +108,8 @@ github.com/metacubex/quic-go v0.41.1-0.20240307164142-46c6f7cdf2d1 h1:63zKmEWU4M github.com/metacubex/quic-go v0.41.1-0.20240307164142-46c6f7cdf2d1/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-20240308143007-4dd80423c25a h1:ATj0jL+cp7n+NT3T010cXK5KoVvAbeGhZFtUFHvq2BU= -github.com/metacubex/sing-quic v0.0.0-20240308143007-4dd80423c25a/go.mod h1:WyY0zYxv+o+18R/Ece+QFontlgXoobKbNqbtYn2zjz8= +github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01 h1:5INHs85Gp1JZsdF7fQp1pXUjfJOX2dhwZjuUQWJVSt8= +github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01/go.mod h1:WyY0zYxv+o+18R/Ece+QFontlgXoobKbNqbtYn2zjz8= 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 44d8a1462941d700c11e5f9b8842a0aa6a87bd81 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Tue, 12 Mar 2024 03:14:25 +0800 Subject: [PATCH 46/65] feat: add `IP-ASN` rule --- component/geodata/init.go | 33 +++++++++++++-- component/mmdb/mmdb.go | 75 +++++++++++++++++++++++++-------- component/mmdb/patch_android.go | 10 ++--- component/mmdb/reader.go | 19 ++++++++- config/config.go | 3 ++ config/update_geo.go | 23 +++++++++- constant/geodata.go | 2 + constant/metadata.go | 3 +- constant/path.go | 20 +++++++++ constant/rule.go | 3 ++ dns/filters.go | 2 +- docs/config.yaml | 16 +++++++ rules/common/geoip.go | 2 +- rules/common/ipasn.go | 67 +++++++++++++++++++++++++++++ rules/parser.go | 3 ++ 15 files changed, 248 insertions(+), 33 deletions(-) create mode 100644 rules/common/ipasn.go diff --git a/component/geodata/init.go b/component/geodata/init.go index 842efcc5..834567a4 100644 --- a/component/geodata/init.go +++ b/component/geodata/init.go @@ -14,8 +14,11 @@ import ( "github.com/metacubex/mihomo/log" ) -var initGeoSite bool -var initGeoIP int +var ( + initGeoSite bool + initGeoIP int + initASN bool +) func InitGeoSite() error { if _, err := os.Stat(C.Path.GeoSite()); os.IsNotExist(err) { @@ -113,7 +116,7 @@ func InitGeoIP() error { } if initGeoIP != 2 { - if !mmdb.Verify() { + if !mmdb.Verify(C.Path.MMDB()) { log.Warnln("MMDB invalid, remove and download") if err := os.Remove(C.Path.MMDB()); err != nil { return fmt.Errorf("can't remove invalid MMDB: %s", err.Error()) @@ -126,3 +129,27 @@ func InitGeoIP() error { } return nil } + +func InitASN() error { + if _, err := os.Stat(C.Path.ASN()); os.IsNotExist(err) { + log.Infoln("Can't find ASN.mmdb, start download") + if err := mmdb.DownloadASN(C.Path.ASN()); err != nil { + return fmt.Errorf("can't download ASN.mmdb: %s", err.Error()) + } + log.Infoln("Download ASN.mmdb finish") + initASN = false + } + if !initASN { + if !mmdb.Verify(C.Path.ASN()) { + log.Warnln("ASN invalid, remove and download") + if err := os.Remove(C.Path.ASN()); err != nil { + return fmt.Errorf("can't remove invalid ASN: %s", err.Error()) + } + if err := mmdb.DownloadASN(C.Path.ASN()); err != nil { + return fmt.Errorf("can't download ASN: %s", err.Error()) + } + } + initASN = true + } + return nil +} diff --git a/component/mmdb/mmdb.go b/component/mmdb/mmdb.go index 66b632be..81156bc6 100644 --- a/component/mmdb/mmdb.go +++ b/component/mmdb/mmdb.go @@ -25,56 +25,58 @@ const ( ) var ( - reader Reader - once sync.Once + IPreader IPReader + ASNreader ASNReader + IPonce sync.Once + ASNonce sync.Once ) func LoadFromBytes(buffer []byte) { - once.Do(func() { + IPonce.Do(func() { mmdb, err := maxminddb.FromBytes(buffer) if err != nil { log.Fatalln("Can't load mmdb: %s", err.Error()) } - reader = Reader{Reader: mmdb} + IPreader = IPReader{Reader: mmdb} switch mmdb.Metadata.DatabaseType { case "sing-geoip": - reader.databaseType = typeSing + IPreader.databaseType = typeSing case "Meta-geoip0": - reader.databaseType = typeMetaV0 + IPreader.databaseType = typeMetaV0 default: - reader.databaseType = typeMaxmind + IPreader.databaseType = typeMaxmind } }) } -func Verify() bool { - instance, err := maxminddb.Open(C.Path.MMDB()) +func Verify(path string) bool { + instance, err := maxminddb.Open(path) if err == nil { instance.Close() } return err == nil } -func Instance() Reader { - once.Do(func() { +func IPInstance() IPReader { + IPonce.Do(func() { mmdbPath := C.Path.MMDB() log.Infoln("Load MMDB file: %s", mmdbPath) mmdb, err := maxminddb.Open(mmdbPath) if err != nil { log.Fatalln("Can't load MMDB: %s", err.Error()) } - reader = Reader{Reader: mmdb} + IPreader = IPReader{Reader: mmdb} switch mmdb.Metadata.DatabaseType { case "sing-geoip": - reader.databaseType = typeSing + IPreader.databaseType = typeSing case "Meta-geoip0": - reader.databaseType = typeMetaV0 + IPreader.databaseType = typeMetaV0 default: - reader.databaseType = typeMaxmind + IPreader.databaseType = typeMaxmind } }) - return reader + return IPreader } func DownloadMMDB(path string) (err error) { @@ -96,6 +98,43 @@ func DownloadMMDB(path string) (err error) { return err } -func Reload() { - mihomoOnce.Reset(&once) +func ASNInstance() ASNReader { + ASNonce.Do(func() { + ASNPath := C.Path.ASN() + log.Infoln("Load ASN file: %s", ASNPath) + asn, err := maxminddb.Open(ASNPath) + if err != nil { + log.Fatalln("Can't load ASN: %s", err.Error()) + } + ASNreader = ASNReader{Reader: asn} + }) + + return ASNreader +} + +func DownloadASN(path string) (err error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) + defer cancel() + resp, err := mihomoHttp.HttpRequest(ctx, C.ASNUrl, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) + if err != nil { + return + } + defer resp.Body.Close() + + f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o644) + if err != nil { + return err + } + defer f.Close() + _, err = io.Copy(f, resp.Body) + + return err +} + +func ReloadIP() { + mihomoOnce.Reset(&IPonce) +} + +func ReloadASN() { + mihomoOnce.Reset(&ASNonce) } diff --git a/component/mmdb/patch_android.go b/component/mmdb/patch_android.go index a994b75e..147a3324 100644 --- a/component/mmdb/patch_android.go +++ b/component/mmdb/patch_android.go @@ -5,14 +5,14 @@ package mmdb import "github.com/oschwald/maxminddb-golang" func InstallOverride(override *maxminddb.Reader) { - newReader := Reader{Reader: override} + newReader := IPReader{Reader: override} switch override.Metadata.DatabaseType { case "sing-geoip": - reader.databaseType = typeSing + IPreader.databaseType = typeSing case "Meta-geoip0": - reader.databaseType = typeMetaV0 + IPreader.databaseType = typeMetaV0 default: - reader.databaseType = typeMaxmind + IPreader.databaseType = typeMaxmind } - reader = newReader + IPreader = newReader } diff --git a/component/mmdb/reader.go b/component/mmdb/reader.go index 787bdfe8..e76e9939 100644 --- a/component/mmdb/reader.go +++ b/component/mmdb/reader.go @@ -14,12 +14,21 @@ type geoip2Country struct { } `maxminddb:"country"` } -type Reader struct { +type IPReader struct { *maxminddb.Reader databaseType } -func (r Reader) LookupCode(ipAddress net.IP) []string { +type ASNReader struct { + *maxminddb.Reader +} + +type ASNResult struct { + AutonomousSystemNumber uint32 `maxminddb:"autonomous_system_number"` + AutonomousSystemOrganization string `maxminddb:"autonomous_system_organization"` +} + +func (r IPReader) LookupCode(ipAddress net.IP) []string { switch r.databaseType { case typeMaxmind: var country geoip2Country @@ -56,3 +65,9 @@ func (r Reader) LookupCode(ipAddress net.IP) []string { panic(fmt.Sprint("unknown geoip database type:", r.databaseType)) } } + +func (r ASNReader) LookupASN(ip net.IP) ASNResult { + var result ASNResult + r.Lookup(ip, &result) + return result +} diff --git a/config/config.go b/config/config.go index 8fec0bab..ca866491 100644 --- a/config/config.go +++ b/config/config.go @@ -348,6 +348,7 @@ type RawConfig struct { type GeoXUrl struct { GeoIp string `yaml:"geoip" json:"geoip"` Mmdb string `yaml:"mmdb" json:"mmdb"` + ASN string `yaml:"asn" json:"asn"` GeoSite string `yaml:"geosite" json:"geosite"` } @@ -495,6 +496,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { }, GeoXUrl: GeoXUrl{ Mmdb: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb", + ASN: "https://github.com/xishang0128/geoip/releases/download/latest/GeoLite2-ASN.mmdb", GeoIp: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat", GeoSite: "https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat", }, @@ -620,6 +622,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) { C.GeoIpUrl = cfg.GeoXUrl.GeoIp C.GeoSiteUrl = cfg.GeoXUrl.GeoSite C.MmdbUrl = cfg.GeoXUrl.Mmdb + C.ASNUrl = cfg.GeoXUrl.ASN C.GeodataMode = cfg.GeodataMode C.UA = cfg.GlobalUA if cfg.KeepAliveInterval != 0 { diff --git a/config/update_geo.go b/config/update_geo.go index bf3d0810..43cac25c 100644 --- a/config/update_geo.go +++ b/config/update_geo.go @@ -34,7 +34,7 @@ func UpdateGeoDatabases() error { } } else { - defer mmdb.Reload() + defer mmdb.ReloadIP() data, err := downloadForBytes(C.MmdbUrl) if err != nil { return fmt.Errorf("can't download MMDB database file: %w", err) @@ -46,12 +46,31 @@ func UpdateGeoDatabases() error { } _ = instance.Close() - mmdb.Instance().Reader.Close() // mmdb is loaded with mmap, so it needs to be closed before overwriting the file + mmdb.IPInstance().Reader.Close() // mmdb is loaded with mmap, so it needs to be closed before overwriting the file if err = saveFile(data, C.Path.MMDB()); err != nil { return fmt.Errorf("can't save MMDB database file: %w", err) } } + if C.ASNEnable { + defer mmdb.ReloadASN() + data, err := downloadForBytes(C.ASNUrl) + if err != nil { + return fmt.Errorf("can't download ASN database file: %w", err) + } + + instance, err := maxminddb.FromBytes(data) + if err != nil { + return fmt.Errorf("invalid ASN database file: %s", err) + } + _ = instance.Close() + + mmdb.ASNInstance().Reader.Close() + if err = saveFile(data, C.Path.ASN()); err != nil { + return fmt.Errorf("can't save ASN database file: %w", err) + } + } + data, err := downloadForBytes(C.GeoSiteUrl) if err != nil { return fmt.Errorf("can't download GeoSite database file: %w", err) diff --git a/constant/geodata.go b/constant/geodata.go index e93d56b3..cd3f74e3 100644 --- a/constant/geodata.go +++ b/constant/geodata.go @@ -1,10 +1,12 @@ package constant var ( + ASNEnable bool GeodataMode bool GeoAutoUpdate bool GeoUpdateInterval int GeoIpUrl string MmdbUrl string GeoSiteUrl string + ASNUrl string ) diff --git a/constant/metadata.go b/constant/metadata.go index bf0fa281..381e2dd4 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -133,7 +133,8 @@ type Metadata struct { Type Type `json:"type"` SrcIP netip.Addr `json:"sourceIP"` DstIP netip.Addr `json:"destinationIP"` - DstGeoIP []string `json:"destinationGeoIP"` // can be nil if never queried, empty slice if got no result + DstGeoIP []string `json:"destinationGeoIP"` // can be nil if never queried, empty slice if got no result + DstIPASN string `json:"destinationIPASN"` SrcPort uint16 `json:"sourcePort,string"` // `,string` is used to compatible with old version json output DstPort uint16 `json:"destinationPort,string"` // `,string` is used to compatible with old version json output InIP netip.Addr `json:"inboundIP"` diff --git a/constant/path.go b/constant/path.go index a920fbbc..77f7d0ef 100644 --- a/constant/path.go +++ b/constant/path.go @@ -15,6 +15,7 @@ const Name = "mihomo" var ( GeositeName = "GeoSite.dat" GeoipName = "GeoIP.dat" + ASNName = "ASN.mmdb" ) // Path is used to get the configuration path @@ -112,6 +113,25 @@ func (p *path) MMDB() string { return P.Join(p.homeDir, "geoip.metadb") } +func (p *path) ASN() string { + files, err := os.ReadDir(p.homeDir) + if err != nil { + return "" + } + for _, fi := range files { + if fi.IsDir() { + // 目录则直接跳过 + continue + } else { + if strings.EqualFold(fi.Name(), "ASN.mmdb") { + ASNName = fi.Name() + return P.Join(p.homeDir, fi.Name()) + } + } + } + return P.Join(p.homeDir, ASNName) +} + func (p *path) OldCache() string { return P.Join(p.homeDir, ".cache") } diff --git a/constant/rule.go b/constant/rule.go index 9b038221..fcefaba6 100644 --- a/constant/rule.go +++ b/constant/rule.go @@ -9,6 +9,7 @@ const ( GEOSITE GEOIP IPCIDR + IPASN SrcIPCIDR IPSuffix SrcIPSuffix @@ -49,6 +50,8 @@ func (rt RuleType) String() string { return "GeoIP" case IPCIDR: return "IPCIDR" + case IPASN: + return "IPASN" case SrcIPCIDR: return "SrcIPCIDR" case IPSuffix: diff --git a/dns/filters.go b/dns/filters.go index d8633e8b..138f3429 100644 --- a/dns/filters.go +++ b/dns/filters.go @@ -24,7 +24,7 @@ var geoIPMatcher *router.GeoIPMatcher func (gf *geoipFilter) Match(ip netip.Addr) bool { if !C.GeodataMode { - codes := mmdb.Instance().LookupCode(ip.AsSlice()) + codes := mmdb.IPInstance().LookupCode(ip.AsSlice()) for _, code := range codes { if !strings.EqualFold(code, gf.code) && !ip.IsPrivate() { return true diff --git a/docs/config.yaml b/docs/config.yaml index dc257ee2..d912eb65 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -674,6 +674,8 @@ proxies: # socks5 type: hysteria2 server: server.com port: 443 + # ports: 1000,2000-3000,5000 # port 不可省略 + # hop-interval: 15 # up和down均不写或为0则使用BBR流控 # up: "30 Mbps" # 若不写单位,默认为 Mbps # down: "200 Mbps" # 若不写单位,默认为 Mbps @@ -767,6 +769,18 @@ proxies: # socks5 # protocol-param: "#" # udp: true + - name: "ssh-out" + type: ssh + + server: 127.0.0.1 + port: 22 + username: root + password: password + privateKey: path + +# dns出站会将请求劫持到内部dns模块,所有请求均在内部处理 + - name: "dns-out" + type: dns proxy-groups: # 代理链,目前relay可以支持udp的只有vmess/vless/trojan/ss/ssr/tuic # wireguard目前不支持在relay中使用,请使用proxy中的dialer-proxy配置项 @@ -885,6 +899,8 @@ rule-providers: type: file rules: - RULE-SET,rule1,REJECT + - IP-ASN,1,PROXY + - DOMAIN-REGEX,^abc,DIRECT - DOMAIN-SUFFIX,baidu.com,DIRECT - DOMAIN-KEYWORD,google,ss1 - IP-CIDR,1.1.1.1/32,ss1 diff --git a/rules/common/geoip.go b/rules/common/geoip.go index 2a891313..223a7904 100644 --- a/rules/common/geoip.go +++ b/rules/common/geoip.go @@ -52,7 +52,7 @@ func (g *GEOIP) Match(metadata *C.Metadata) (bool, string) { if metadata.DstGeoIP != nil { return false, g.adapter } - metadata.DstGeoIP = mmdb.Instance().LookupCode(ip.AsSlice()) + metadata.DstGeoIP = mmdb.IPInstance().LookupCode(ip.AsSlice()) for _, code := range metadata.DstGeoIP { if g.country == code { return true, g.adapter diff --git a/rules/common/ipasn.go b/rules/common/ipasn.go new file mode 100644 index 00000000..1fce8af4 --- /dev/null +++ b/rules/common/ipasn.go @@ -0,0 +1,67 @@ +package common + +import ( + "strconv" + + "github.com/metacubex/mihomo/component/geodata" + "github.com/metacubex/mihomo/component/mmdb" + C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/log" +) + +type ASN struct { + *Base + asn string + adapter string + noResolveIP bool +} + +func (a *ASN) Match(metadata *C.Metadata) (bool, string) { + ip := metadata.DstIP + if !ip.IsValid() { + return false, "" + } + + result := mmdb.ASNInstance().LookupASN(ip.AsSlice()) + + asnNumber := strconv.FormatUint(uint64(result.AutonomousSystemNumber), 10) + metadata.DstIPASN = asnNumber + " " + result.AutonomousSystemOrganization + + match := a.asn == asnNumber + return match, a.adapter +} + +func (a *ASN) RuleType() C.RuleType { + return C.IPASN +} + +func (a *ASN) Adapter() string { + return a.adapter +} + +func (a *ASN) Payload() string { + return a.asn +} + +func (a *ASN) ShouldResolveIP() bool { + return !a.noResolveIP +} + +func (a *ASN) GetASN() string { + return a.asn +} + +func NewIPASN(asn string, adapter string, noResolveIP bool) (*ASN, error) { + C.ASNEnable = true + if err := geodata.InitASN(); err != nil { + log.Errorln("can't initial ASN: %s", err) + return nil, err + } + + return &ASN{ + Base: &Base{}, + asn: asn, + adapter: adapter, + noResolveIP: noResolveIP, + }, nil +} diff --git a/rules/parser.go b/rules/parser.go index f7df5f49..b69cc4fd 100644 --- a/rules/parser.go +++ b/rules/parser.go @@ -27,6 +27,9 @@ func ParseRule(tp, payload, target string, params []string, subRules map[string] case "IP-CIDR", "IP-CIDR6": noResolve := RC.HasNoResolve(params) parsed, parseErr = RC.NewIPCIDR(payload, target, RC.WithIPCIDRNoResolve(noResolve)) + case "IP-ASN": + noResolve := RC.HasNoResolve(params) + parsed, parseErr = RC.NewIPASN(payload, target, noResolve) case "SRC-IP-CIDR": parsed, parseErr = RC.NewIPCIDR(payload, target, RC.WithIPCIDRSourceIP(true), RC.WithIPCIDRNoResolve(true)) case "IP-SUFFIX": From 012e4485621939af60bbd8708c4d15c8a04039e9 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 12 Mar 2024 15:06:41 +0800 Subject: [PATCH 47/65] fix: when hysteria2 set `ports`, `port` can be empty --- adapter/outbound/hysteria2.go | 59 ++++++++++------------------------- common/utils/ranges.go | 16 ++++++++-- 2 files changed, 30 insertions(+), 45 deletions(-) diff --git a/adapter/outbound/hysteria2.go b/adapter/outbound/hysteria2.go index 5c817373..0ad7c214 100644 --- a/adapter/outbound/hysteria2.go +++ b/adapter/outbound/hysteria2.go @@ -8,10 +8,10 @@ import ( "net" "runtime" "strconv" - "strings" "time" CN "github.com/metacubex/mihomo/common/net" + "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/ca" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/proxydialer" @@ -44,7 +44,7 @@ type Hysteria2Option struct { BasicOption Name string `proxy:"name"` Server string `proxy:"server"` - Port int `proxy:"port"` + Port int `proxy:"port,omitempty"` Ports string `proxy:"ports,omitempty"` HopInterval int `proxy:"hop-interval,omitempty"` Up string `proxy:"up,omitempty"` @@ -91,41 +91,6 @@ func closeHysteria2(h *Hysteria2) { } } -func parsePorts(portStr string) (ports []uint16) { - portStrs := strings.Split(portStr, ",") - for _, portStr := range portStrs { - if strings.Contains(portStr, "-") { - // Port range - portRange := strings.Split(portStr, "-") - if len(portRange) != 2 { - return nil - } - start, err := strconv.ParseUint(portRange[0], 10, 16) - if err != nil { - return nil - } - end, err := strconv.ParseUint(portRange[1], 10, 16) - if err != nil { - return nil - } - if start > end { - start, end = end, start - } - for i := start; i <= end; i++ { - ports = append(ports, uint16(i)) - } - } else { - // Single port - port, err := strconv.ParseUint(portStr, 10, 16) - if err != nil { - return nil - } - ports = append(ports, uint16(port)) - } - } - return ports -} - func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) var salamanderPassword string @@ -187,13 +152,18 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { }, } + var ranges utils.IntRanges[uint16] + var serverAddress []string if option.Ports != "" { - ports := parsePorts(option.Ports) - if len(ports) > 0 { - serverAddress := make([]string, len(ports)) - for i, port := range ports { - serverAddress[i] = net.JoinHostPort(option.Server, strconv.Itoa(int(port))) - } + ranges, err = utils.NewUnsignedRanges[uint16](option.Ports) + if err != nil { + return nil, err + } + ranges.Range(func(port uint16) bool { + serverAddress = append(serverAddress, net.JoinHostPort(option.Server, strconv.Itoa(int(port)))) + return true + }) + if len(serverAddress) > 0 { clientOptions.ServerAddress = func(ctx context.Context) (*net.UDPAddr, error) { return resolveUDPAddrWithPrefer(ctx, "udp", serverAddress[fastrand.Intn(len(serverAddress))], C.NewDNSPrefer(option.IPVersion)) } @@ -206,6 +176,9 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { clientOptions.HopInterval = time.Duration(option.HopInterval) * time.Second } } + if option.Port == 0 && len(serverAddress) == 0 { + return nil, errors.New("invalid port") + } client, err := hysteria2.NewClient(clientOptions) if err != nil { diff --git a/common/utils/ranges.go b/common/utils/ranges.go index 810105ff..e656e34b 100644 --- a/common/utils/ranges.go +++ b/common/utils/ranges.go @@ -20,6 +20,8 @@ func newIntRanges[T constraints.Integer](expected string, parseFn func(string) ( return nil, nil } + // support: 200,302 or 200,204,401-429,501-503 + expected = strings.ReplaceAll(expected, ",", "/") list := strings.Split(expected, "/") if len(list) > 28 { return nil, fmt.Errorf("%w, too many ranges to use, maximum support 28 ranges", errIntRanges) @@ -47,9 +49,9 @@ func newIntRangesFromList[T constraints.Integer](list []string, parseFn func(str } switch statusLen { - case 1: + case 1: // Port range ranges = append(ranges, NewRange(T(start), T(start))) - case 2: + case 2: // Single port end, err := parseFn(strings.Trim(status[1], "[ ]")) if err != nil { return nil, errIntRanges @@ -130,3 +132,13 @@ func (ranges IntRanges[T]) ToString() string { return strings.Join(terms, "/") } + +func (ranges IntRanges[T]) Range(f func(t T) bool) { + for _, r := range ranges { + for i := r.Start(); i <= r.End(); i++ { + if !f(i) { + return + } + } + } +} From 81c832ef9ef97265da9e59298b3642554ca17fe7 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 12 Mar 2024 15:14:56 +0800 Subject: [PATCH 48/65] chore: code cleanup --- adapter/provider/provider.go | 14 ++------------ common/utils/ranges.go | 6 +++++- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index b1209d22..2715a309 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -46,18 +46,13 @@ type proxySetProvider struct { } func (pp *proxySetProvider) MarshalJSON() ([]byte, error) { - expectedStatus := "*" - if pp.healthCheck.expectedStatus != nil { - expectedStatus = pp.healthCheck.expectedStatus.ToString() - } - return json.Marshal(map[string]any{ "name": pp.Name(), "type": pp.Type().String(), "vehicleType": pp.VehicleType().String(), "proxies": pp.Proxies(), "testUrl": pp.healthCheck.url, - "expectedStatus": expectedStatus, + "expectedStatus": pp.healthCheck.expectedStatus.String(), "updatedAt": pp.UpdatedAt, "subscriptionInfo": pp.subscriptionInfo, }) @@ -221,18 +216,13 @@ type compatibleProvider struct { } func (cp *compatibleProvider) MarshalJSON() ([]byte, error) { - expectedStatus := "*" - if cp.healthCheck.expectedStatus != nil { - expectedStatus = cp.healthCheck.expectedStatus.ToString() - } - return json.Marshal(map[string]any{ "name": cp.Name(), "type": cp.Type().String(), "vehicleType": cp.VehicleType().String(), "proxies": cp.Proxies(), "testUrl": cp.healthCheck.url, - "expectedStatus": expectedStatus, + "expectedStatus": cp.healthCheck.expectedStatus.String(), }) } diff --git a/common/utils/ranges.go b/common/utils/ranges.go index e656e34b..c71f84c9 100644 --- a/common/utils/ranges.go +++ b/common/utils/ranges.go @@ -110,7 +110,7 @@ func (ranges IntRanges[T]) Check(status T) bool { return false } -func (ranges IntRanges[T]) ToString() string { +func (ranges IntRanges[T]) String() string { if len(ranges) == 0 { return "*" } @@ -134,6 +134,10 @@ func (ranges IntRanges[T]) ToString() string { } func (ranges IntRanges[T]) Range(f func(t T) bool) { + if len(ranges) == 0 { + return + } + for _, r := range ranges { for i := r.Start(); i <= r.End(); i++ { if !f(i) { From 5fdfde6a07e32e5c2e35ce68e15d074611578084 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 13 Mar 2024 08:30:41 +0800 Subject: [PATCH 49/65] chore: ssh outbound add `private-key-passphrase`,`host-key`,`host-key-algorithms` rename `privateKey` to `private-key` and support direct write private key value in config file --- adapter/outbound/ssh.go | 75 ++++++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 20 deletions(-) diff --git a/adapter/outbound/ssh.go b/adapter/outbound/ssh.go index a41a8132..a0efabca 100644 --- a/adapter/outbound/ssh.go +++ b/adapter/outbound/ssh.go @@ -1,11 +1,15 @@ package outbound import ( + "bytes" "context" + "encoding/base64" + "fmt" "net" "os" "runtime" "strconv" + "strings" "sync" N "github.com/metacubex/mihomo/common/net" @@ -26,12 +30,15 @@ type Ssh struct { type SshOption struct { BasicOption - Name string `proxy:"name"` - Server string `proxy:"server"` - Port int `proxy:"port"` - UserName string `proxy:"username"` - Password string `proxy:"password,omitempty"` - PrivateKey string `proxy:"privateKey,omitempty"` + Name string `proxy:"name"` + Server string `proxy:"server"` + Port int `proxy:"port"` + UserName string `proxy:"username"` + Password string `proxy:"password,omitempty"` + PrivateKey string `proxy:"private-key,omitempty"` + PrivateKeyPassphrase string `proxy:"private-key-passphrase,omitempty"` + HostKey []string `proxy:"host-key,omitempty"` + HostKeyAlgorithms []string `proxy:"host-key-algorithms,omitempty"` } func (s *Ssh) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { @@ -119,28 +126,56 @@ func NewSsh(option SshOption) (*Ssh, error) { addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) config := ssh.ClientConfig{ - User: option.UserName, - HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { - return nil - }, + User: option.UserName, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + HostKeyAlgorithms: option.HostKeyAlgorithms, } - if option.Password == "" { - b, err := os.ReadFile(option.PrivateKey) - if err != nil { - return nil, err + if option.PrivateKey != "" { + var b []byte + var err error + if strings.Contains(option.PrivateKey, "PRIVATE KEY") { + b = []byte(option.PrivateKey) + } else { + b, err = os.ReadFile(C.Path.Resolve(option.PrivateKey)) + if err != nil { + return nil, err + } + } + var pKey ssh.Signer + if option.PrivateKeyPassphrase != "" { + pKey, err = ssh.ParsePrivateKeyWithPassphrase(b, []byte(option.PrivateKeyPassphrase)) + } else { + pKey, err = ssh.ParsePrivateKey(b) } - pKey, err := ssh.ParsePrivateKey(b) if err != nil { return nil, err } - config.Auth = []ssh.AuthMethod{ - ssh.PublicKeys(pKey), + config.Auth = append(config.Auth, ssh.PublicKeys(pKey)) + } + + if option.Password != "" { + config.Auth = append(config.Auth, ssh.Password(option.Password)) + } + + if len(option.HostKey) != 0 { + keys := make([]ssh.PublicKey, len(option.HostKey)) + for i, hostKey := range option.HostKey { + key, _, _, _, err := ssh.ParseAuthorizedKey([]byte(hostKey)) + if err != nil { + return nil, fmt.Errorf("parse host key :%s", key) + } + keys[i] = key } - } else { - config.Auth = []ssh.AuthMethod{ - ssh.Password(option.Password), + config.HostKeyCallback = func(hostname string, remote net.Addr, key ssh.PublicKey) error { + serverKey := key.Marshal() + for _, hostKey := range keys { + if bytes.Equal(serverKey, hostKey.Marshal()) { + return nil + } + } + return fmt.Errorf("host key mismatch, server send :%s %s", key.Type(), base64.StdEncoding.EncodeToString(serverKey)) } } From 31d3614060ea895a98d2791b22cdc033401fa3fb Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 13 Mar 2024 08:54:50 +0800 Subject: [PATCH 50/65] chore: upgrade dependencies --- go.mod | 36 +++++++++++++++--------------- go.sum | 70 ++++++++++++++++++++++++++++++---------------------------- 2 files changed, 54 insertions(+), 52 deletions(-) diff --git a/go.mod b/go.mod index cdc2c8cb..2d1ac543 100644 --- a/go.mod +++ b/go.mod @@ -8,14 +8,14 @@ require ( github.com/bahlo/generic-list-go v0.2.0 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.11 + github.com/dlclark/regexp2 v1.11.0 + github.com/go-chi/chi/v5 v5.0.12 github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.3 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/klauspost/cpuid/v2 v2.2.6 + github.com/insomniacslk/dhcp v0.0.0-20240227161007-c728f5dd21c8 + github.com/klauspost/cpuid/v2 v2.2.7 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 @@ -27,32 +27,32 @@ require ( github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f github.com/metacubex/sing-wireguard v0.0.0-20231209125515-0594297f7232 github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 - github.com/miekg/dns v1.1.57 + github.com/miekg/dns v1.1.58 github.com/mroth/weightedrand/v2 v2.1.0 github.com/openacid/low v0.1.21 github.com/oschwald/maxminddb-golang v1.12.0 - github.com/puzpuzpuz/xsync/v3 v3.0.2 + github.com/puzpuzpuz/xsync/v3 v3.1.0 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 v0.3.6 github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 github.com/sagernet/sing-shadowtls v0.1.4 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.12 + github.com/shirou/gopsutil/v3 v3.24.2 github.com/sirupsen/logrus v1.9.3 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 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.19.0 - golang.org/x/exp v0.0.0-20240110193028-0dcbfd608b1e - golang.org/x/net v0.21.0 + golang.org/x/crypto v0.21.0 + golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 + golang.org/x/net v0.22.0 golang.org/x/sync v0.6.0 - golang.org/x/sys v0.17.0 - google.golang.org/protobuf v1.32.0 + golang.org/x/sys v0.18.0 + google.golang.org/protobuf v1.33.0 gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.2.1 ) @@ -102,13 +102,13 @@ require ( github.com/tklauser/numcpus v0.6.1 // indirect github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect - github.com/yusufpapurcu/wmi v1.2.3 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect go.uber.org/mock v0.3.0 // indirect - golang.org/x/mod v0.14.0 // indirect + golang.org/x/mod v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.16.1 // indirect + golang.org/x/tools v0.18.0 // indirect ) -replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240111014253-f1818b6a82b2 +replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240313005020-c77f32e55220 diff --git a/go.sum b/go.sum index fe671108..62e8fb69 100644 --- a/go.sum +++ b/go.sum @@ -28,8 +28,8 @@ github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFE github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= -github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8= github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I= github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g= @@ -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.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA= -github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= +github.com/go-chi/chi/v5 v5.0.12/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= @@ -78,16 +78,16 @@ github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA= -github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI= +github.com/insomniacslk/dhcp v0.0.0-20240227161007-c728f5dd21c8 h1:V3plQrMHRWOB5zMm3yNqvBxDQVW1+/wHBSok5uPdmVs= +github.com/insomniacslk/dhcp v0.0.0-20240227161007-c728f5dd21c8/go.mod h1:izxuNQZeFrbx2nK2fAyN5iNUB34Fe9j0nK4PwLzAkKw= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 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/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= -github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= @@ -106,8 +106,8 @@ github.com/metacubex/gvisor v0.0.0-20240214095142-666a73bcf165 h1:QIQI4gEm+gTwVN github.com/metacubex/gvisor v0.0.0-20240214095142-666a73bcf165/go.mod h1:SKY70wiF1UTSoyuDZyKPMsUC6MsMxh8Y3ZNkIa6J3fU= github.com/metacubex/quic-go v0.41.1-0.20240307164142-46c6f7cdf2d1 h1:63zKmEWU4MB5MjUSCmeDhm3OzilF7ypXWPq0gAA2GE8= github.com/metacubex/quic-go v0.41.1-0.20240307164142-46c6f7cdf2d1/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 v0.0.0-20240313005020-c77f32e55220 h1:lZLFR28Jf3gjoHh560uC8AKnWcQ2+VQoQY/rSj6kSrc= +github.com/metacubex/sing v0.0.0-20240313005020-c77f32e55220/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01 h1:5INHs85Gp1JZsdF7fQp1pXUjfJOX2dhwZjuUQWJVSt8= github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01/go.mod h1:WyY0zYxv+o+18R/Ece+QFontlgXoobKbNqbtYn2zjz8= github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ= @@ -122,8 +122,8 @@ github.com/metacubex/sing-wireguard v0.0.0-20231209125515-0594297f7232 h1:loWjR+ github.com/metacubex/sing-wireguard v0.0.0-20231209125515-0594297f7232/go.mod h1:NGCrBZ+fUmp81yaA1kVskcNWBnwl5z4UHxz47A01zm8= github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 h1:as/aO/fM8nv4W4pOr9EETP6kV/Oaujk3fUNyQSJK61c= github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= -github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= -github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= +github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= +github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU= github.com/mroth/weightedrand/v2 v2.1.0/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU= github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 h1:1102pQc2SEPp5+xrS26wEaeb26sZy6k9/ZXlZN+eXE4= @@ -146,8 +146,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= -github.com/puzpuzpuz/xsync/v3 v3.0.2 h1:3yESHrRFYr6xzkz61LLkvNiPFXxJEAABanTQpKbAaew= -github.com/puzpuzpuz/xsync/v3 v3.0.2/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= +github.com/puzpuzpuz/xsync/v3 v3.1.0 h1:EewKT7/LNac5SLiEblJeUu8z5eERHrmRLnMQL2d7qX4= +github.com/puzpuzpuz/xsync/v3 v3.1.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= @@ -171,8 +171,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.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= -github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= +github.com/shirou/gopsutil/v3 v3.24.2 h1:kcR0erMbLg5/3LcInpw0X/rrPSqq4CDPyI6A6ZRC18Y= +github.com/shirou/gopsutil/v3 v3.24.2/go.mod h1:tSg/594BcA+8UdQU2XcW803GWYgdtauFFPgJCJKZlVk= 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= @@ -188,13 +188,15 @@ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= @@ -208,8 +210,8 @@ github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695AP github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= -github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= -github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zhangyunhao116/fastrand v0.3.0 h1:7bwe124xcckPulX6fxtr2lFdO2KQqaefdtbk+mqO/Ig= github.com/zhangyunhao116/fastrand v0.3.0/go.mod h1:0v5KgHho0VE6HU192HnY15de/oDS8UrbBChIFjIhBtc= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo= @@ -222,18 +224,18 @@ 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.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -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/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= +golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= 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/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.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.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -252,22 +254,22 @@ 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/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= 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= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= -golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +golang.org/x/tools v0.18.0 h1:k8NLag8AGHnn+PHbl7g43CtqZAwG60vZkLqgyZgIHgQ= +golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg= 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.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= -google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.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 dceb8ee535dafbc56740c7ab702e067c9ea8ecce Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 13 Mar 2024 14:49:46 +0800 Subject: [PATCH 51/65] fix: resolve atomic.Value usages with interface types --- common/atomic/value.go | 19 +++++++++++++++---- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/common/atomic/value.go b/common/atomic/value.go index cbc6c5b8..36623b3e 100644 --- a/common/atomic/value.go +++ b/common/atomic/value.go @@ -15,20 +15,31 @@ type TypedValue[T any] struct { value atomic.Value } +// tValue is a struct with determined type to resolve atomic.Value usages with interface types +// https://github.com/golang/go/issues/22550 +// +// The intention to have an atomic value store for errors. However, running this code panics: +// panic: sync/atomic: store of inconsistently typed value into Value +// This is because atomic.Value requires that the underlying concrete type be the same (which is a reasonable expectation for its implementation). +// When going through the atomic.Value.Store method call, the fact that both these are of the error interface is lost. +type tValue[T any] struct { + value T +} + func (t *TypedValue[T]) Load() T { value := t.value.Load() if value == nil { return DefaultValue[T]() } - return value.(T) + return value.(tValue[T]).value } func (t *TypedValue[T]) Store(value T) { - t.value.Store(value) + t.value.Store(tValue[T]{value}) } func (t *TypedValue[T]) Swap(new T) T { - old := t.value.Swap(new) + old := t.value.Swap(tValue[T]{new}) if old == nil { return DefaultValue[T]() } @@ -36,7 +47,7 @@ func (t *TypedValue[T]) Swap(new T) T { } func (t *TypedValue[T]) CompareAndSwap(old, new T) bool { - return t.value.CompareAndSwap(old, new) + return t.value.CompareAndSwap(tValue[T]{old}, tValue[T]{new}) } func (t *TypedValue[T]) MarshalJSON() ([]byte, error) { diff --git a/go.mod b/go.mod index 2d1ac543..9becc8c8 100644 --- a/go.mod +++ b/go.mod @@ -111,4 +111,4 @@ require ( golang.org/x/tools v0.18.0 // indirect ) -replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240313005020-c77f32e55220 +replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240313064558-c197257f6542 diff --git a/go.sum b/go.sum index 62e8fb69..7277a32e 100644 --- a/go.sum +++ b/go.sum @@ -106,8 +106,8 @@ github.com/metacubex/gvisor v0.0.0-20240214095142-666a73bcf165 h1:QIQI4gEm+gTwVN github.com/metacubex/gvisor v0.0.0-20240214095142-666a73bcf165/go.mod h1:SKY70wiF1UTSoyuDZyKPMsUC6MsMxh8Y3ZNkIa6J3fU= github.com/metacubex/quic-go v0.41.1-0.20240307164142-46c6f7cdf2d1 h1:63zKmEWU4MB5MjUSCmeDhm3OzilF7ypXWPq0gAA2GE8= github.com/metacubex/quic-go v0.41.1-0.20240307164142-46c6f7cdf2d1/go.mod h1:F/t8VnA47xoia8ABlNA4InkZjssvFJ5p6E6jKdbkgAs= -github.com/metacubex/sing v0.0.0-20240313005020-c77f32e55220 h1:lZLFR28Jf3gjoHh560uC8AKnWcQ2+VQoQY/rSj6kSrc= -github.com/metacubex/sing v0.0.0-20240313005020-c77f32e55220/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= +github.com/metacubex/sing v0.0.0-20240313064558-c197257f6542 h1:e9nBnrJBv3HzZVeSzJN0G2SADjebd2ZLF1F5dmsjUTc= +github.com/metacubex/sing v0.0.0-20240313064558-c197257f6542/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01 h1:5INHs85Gp1JZsdF7fQp1pXUjfJOX2dhwZjuUQWJVSt8= github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01/go.mod h1:WyY0zYxv+o+18R/Ece+QFontlgXoobKbNqbtYn2zjz8= github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwVSgtE9R3QeFQ= From b3db113b1b656ed6de36f643e44f10945f142da6 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 13 Mar 2024 15:32:26 +0800 Subject: [PATCH 52/65] chore: allow disabled system hosts by environment variable `DISABLE_SYSTEM_HOSTS` --- component/resolver/host.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/component/resolver/host.go b/component/resolver/host.go index 69c29a3c..4a429629 100644 --- a/component/resolver/host.go +++ b/component/resolver/host.go @@ -3,6 +3,8 @@ package resolver import ( "errors" "net/netip" + "os" + "strconv" "strings" _ "unsafe" @@ -11,6 +13,8 @@ import ( "github.com/zhangyunhao116/fastrand" ) +var DisableSystemHosts, _ = strconv.ParseBool(os.Getenv("DISABLE_SYSTEM_HOSTS")) + type Hosts struct { *trie.DomainTrie[HostValue] } @@ -47,7 +51,7 @@ func (h *Hosts) Search(domain string, isDomain bool) (*HostValue, bool) { return &hostValue, false } - if !isDomain { + if !isDomain && !DisableSystemHosts { addr, _ := lookupStaticHost(domain) if hostValue, err := NewHostValue(addr); err == nil { return &hostValue, true From c80dd5d738077a53989ddd7e155f685d0e23f1cb Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 19 Mar 2024 14:44:36 +0800 Subject: [PATCH 53/65] chore: retry DNS over TCP when receive a truncated UDP response https://github.com/MetaCubeX/mihomo/issues/1117 --- dns/client.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/dns/client.go b/dns/client.go index 95f0f29b..fc76c124 100644 --- a/dns/client.go +++ b/dns/client.go @@ -12,6 +12,7 @@ import ( "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/resolver" C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/log" D "github.com/miekg/dns" "github.com/zhangyunhao116/fastrand" @@ -97,12 +98,22 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) conn = tls.Client(conn, ca.GetGlobalTLSConfig(c.Client.TLSConfig)) } - msg, _, err := c.Client.ExchangeWithConn(m, &D.Conn{ + dConn := &D.Conn{ Conn: conn, UDPSize: c.Client.UDPSize, TsigSecret: c.Client.TsigSecret, TsigProvider: c.Client.TsigProvider, - }) + } + + msg, _, err := c.Client.ExchangeWithConn(m, dConn) + + // Resolvers MUST resend queries over TCP if they receive a truncated UDP response (with TC=1 set)! + if msg != nil && msg.Truncated && c.Client.Net == "" { + tcpClient := *c.Client // copy a client + tcpClient.Net = "tcp" + log.Debugln("[DNS] Truncated reply from %s:%s for %s over UDP, retrying over TCP", c.host, c.port, m.Question[0].String()) + msg, _, err = tcpClient.ExchangeWithConn(m, dConn) + } ch <- result{msg, err} }() From 80408855acf92409e378fc526bbc766269c92eb6 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 19 Mar 2024 15:18:00 +0800 Subject: [PATCH 54/65] chore: update quic-go to 0.42.0 --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 9becc8c8..b6f5ff54 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ 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.41.1-0.20240307164142-46c6f7cdf2d1 + github.com/metacubex/quic-go v0.42.1-0.20240319071510-a251e5c66a5c github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 @@ -104,7 +104,7 @@ require ( github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect - go.uber.org/mock v0.3.0 // indirect + go.uber.org/mock v0.4.0 // indirect golang.org/x/mod v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect diff --git a/go.sum b/go.sum index 7277a32e..7c1e2425 100644 --- a/go.sum +++ b/go.sum @@ -104,8 +104,8 @@ 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-20240214095142-666a73bcf165 h1:QIQI4gEm+gTwVNdiAyF4EIz5cHm7kSlfDGFpYlAa5dg= github.com/metacubex/gvisor v0.0.0-20240214095142-666a73bcf165/go.mod h1:SKY70wiF1UTSoyuDZyKPMsUC6MsMxh8Y3ZNkIa6J3fU= -github.com/metacubex/quic-go v0.41.1-0.20240307164142-46c6f7cdf2d1 h1:63zKmEWU4MB5MjUSCmeDhm3OzilF7ypXWPq0gAA2GE8= -github.com/metacubex/quic-go v0.41.1-0.20240307164142-46c6f7cdf2d1/go.mod h1:F/t8VnA47xoia8ABlNA4InkZjssvFJ5p6E6jKdbkgAs= +github.com/metacubex/quic-go v0.42.1-0.20240319071510-a251e5c66a5c h1:AhaPKvVqF3N/jXFmlW51Cf1+KddslKAsZqcdgGhZjr0= +github.com/metacubex/quic-go v0.42.1-0.20240319071510-a251e5c66a5c/go.mod h1:iGx3Y1zynls/FjFgykLSqDcM81U0IKePRTXEz5g3iiQ= github.com/metacubex/sing v0.0.0-20240313064558-c197257f6542 h1:e9nBnrJBv3HzZVeSzJN0G2SADjebd2ZLF1F5dmsjUTc= github.com/metacubex/sing v0.0.0-20240313064558-c197257f6542/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01 h1:5INHs85Gp1JZsdF7fQp1pXUjfJOX2dhwZjuUQWJVSt8= @@ -218,8 +218,8 @@ gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiV gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ= go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= -go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= -go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M= 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= From 143fe84b8e78d5e78fc150731d634c6438b269e5 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 20 Mar 2024 09:30:00 +0800 Subject: [PATCH 55/65] chore: update gvisor --- go.mod | 7 +++---- go.sum | 14 ++++++-------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index b6f5ff54..c86ce9ce 100644 --- a/go.mod +++ b/go.mod @@ -23,9 +23,9 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240310154810-47bca850fc01 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 - github.com/metacubex/sing-tun v0.2.1-0.20240214100323-23e40bfb9067 + github.com/metacubex/sing-tun v0.2.1-0.20240320004934-5d2b35447bfd github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f - github.com/metacubex/sing-wireguard v0.0.0-20231209125515-0594297f7232 + github.com/metacubex/sing-wireguard v0.0.0-20240320005523-c46db1e2d1be github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 github.com/miekg/dns v1.1.58 github.com/mroth/weightedrand/v2 v2.1.0 @@ -84,7 +84,7 @@ require ( github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mdlayher/socket v0.4.1 // indirect - github.com/metacubex/gvisor v0.0.0-20240214095142-666a73bcf165 // indirect + github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec // indirect github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/pierrec/lz4/v4 v4.1.14 // indirect @@ -93,7 +93,6 @@ require ( github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qtls-go1-20 v0.4.1 // indirect github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect - github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect diff --git a/go.sum b/go.sum index 7c1e2425..acb0b28a 100644 --- a/go.sum +++ b/go.sum @@ -102,8 +102,8 @@ github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI= github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88= -github.com/metacubex/gvisor v0.0.0-20240214095142-666a73bcf165 h1:QIQI4gEm+gTwVNdiAyF4EIz5cHm7kSlfDGFpYlAa5dg= -github.com/metacubex/gvisor v0.0.0-20240214095142-666a73bcf165/go.mod h1:SKY70wiF1UTSoyuDZyKPMsUC6MsMxh8Y3ZNkIa6J3fU= +github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec h1:HxreOiFTUrJXJautEo8rnE1uKTVGY8wtZepY1Tii/Nc= +github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= github.com/metacubex/quic-go v0.42.1-0.20240319071510-a251e5c66a5c h1:AhaPKvVqF3N/jXFmlW51Cf1+KddslKAsZqcdgGhZjr0= github.com/metacubex/quic-go v0.42.1-0.20240319071510-a251e5c66a5c/go.mod h1:iGx3Y1zynls/FjFgykLSqDcM81U0IKePRTXEz5g3iiQ= github.com/metacubex/sing v0.0.0-20240313064558-c197257f6542 h1:e9nBnrJBv3HzZVeSzJN0G2SADjebd2ZLF1F5dmsjUTc= @@ -114,12 +114,12 @@ 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.1-0.20240214100323-23e40bfb9067 h1:sB9Hiq/Fgq94WD1mAFDUTDaQAJ6y3WZ5nZMEavcK0/o= -github.com/metacubex/sing-tun v0.2.1-0.20240214100323-23e40bfb9067/go.mod h1:HWyO52kAVvuSUN2nms4ZlRfiAwgXO9wGQBJFjymqvOQ= +github.com/metacubex/sing-tun v0.2.1-0.20240320004934-5d2b35447bfd h1:NgLb6Lvr8ZxX0inWswVYjal2SUzsJJ54dFQNOluUJuE= +github.com/metacubex/sing-tun v0.2.1-0.20240320004934-5d2b35447bfd/go.mod h1:GfLZG/QgGpW9+BPjltzONrL5vVms86TWqmZ23J68ISc= 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= -github.com/metacubex/sing-wireguard v0.0.0-20231209125515-0594297f7232/go.mod h1:NGCrBZ+fUmp81yaA1kVskcNWBnwl5z4UHxz47A01zm8= +github.com/metacubex/sing-wireguard v0.0.0-20240320005523-c46db1e2d1be h1:xZHAKZPMdVIwk8zCbmL6vi87xC4VRKWhNLRoO3TdRwA= +github.com/metacubex/sing-wireguard v0.0.0-20240320005523-c46db1e2d1be/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo= github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 h1:as/aO/fM8nv4W4pOr9EETP6kV/Oaujk3fUNyQSJK61c= github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= @@ -169,8 +169,6 @@ github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e h1:iGH0RMv2F github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e/go.mod h1:YbL4TKHRR6APYQv3U2RGfwLDpPYSyWz6oUlpISBEzBE= 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.24.2 h1:kcR0erMbLg5/3LcInpw0X/rrPSqq4CDPyI6A6ZRC18Y= github.com/shirou/gopsutil/v3 v3.24.2/go.mod h1:tSg/594BcA+8UdQU2XcW803GWYgdtauFFPgJCJKZlVk= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= From 7fd5902e6bce535deedd693137b7fe4c90edd380 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 20 Mar 2024 11:30:39 +0800 Subject: [PATCH 56/65] chore: wireguard outbound only can set `ip` and `ipv6` outside `peers` https://github.com/MetaCubeX/mihomo/issues/522 --- adapter/outbound/wireguard.go | 23 +++++++---------------- docs/config.yaml | 4 +--- 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 7c021c87..79803842 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -47,6 +47,8 @@ type WireGuardOption struct { BasicOption WireGuardPeerOption Name string `proxy:"name"` + Ip string `proxy:"ip,omitempty"` + Ipv6 string `proxy:"ipv6,omitempty"` PrivateKey string `proxy:"private-key"` Workers int `proxy:"workers,omitempty"` MTU int `proxy:"mtu,omitempty"` @@ -62,8 +64,6 @@ type WireGuardOption struct { type WireGuardPeerOption struct { Server string `proxy:"server"` Port int `proxy:"port"` - Ip string `proxy:"ip,omitempty"` - Ipv6 string `proxy:"ipv6,omitempty"` PublicKey string `proxy:"public-key,omitempty"` PreSharedKey string `proxy:"pre-shared-key,omitempty"` Reserved []uint8 `proxy:"reserved,omitempty"` @@ -98,7 +98,7 @@ func (option WireGuardPeerOption) Addr() M.Socksaddr { return M.ParseSocksaddrHostPort(option.Server, uint16(option.Port)) } -func (option WireGuardPeerOption) Prefixes() ([]netip.Prefix, error) { +func (option WireGuardOption) Prefixes() ([]netip.Prefix, error) { localPrefixes := make([]netip.Prefix, 0, 2) if len(option.Ip) > 0 { if !strings.Contains(option.Ip, "/") { @@ -160,7 +160,10 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { } outbound.bind = wireguard.NewClientBind(context.Background(), wgSingErrorHandler{outbound.Name()}, outbound.dialer, isConnect, connectAddr, reserved) - var localPrefixes []netip.Prefix + localPrefixes, err := option.Prefixes() + if err != nil { + return nil, err + } var privateKey string { @@ -172,7 +175,6 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { } ipcConf := "private_key=" + privateKey if peersLen := len(option.Peers); peersLen > 0 { - localPrefixes = make([]netip.Prefix, 0, peersLen*2) for i, peer := range option.Peers { var peerPublicKey, preSharedKey string { @@ -208,11 +210,6 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { copy(reserved[:], option.Reserved) outbound.bind.SetReservedForEndpoint(destination, reserved) } - prefixes, err := peer.Prefixes() - if err != nil { - return nil, err - } - localPrefixes = append(localPrefixes, prefixes...) } } else { var peerPublicKey, preSharedKey string @@ -235,11 +232,6 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { if preSharedKey != "" { ipcConf += "\npreshared_key=" + preSharedKey } - var err error - localPrefixes, err = option.Prefixes() - if err != nil { - return nil, err - } var has4, has6 bool for _, address := range localPrefixes { if address.Addr().Is4() { @@ -266,7 +258,6 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { if len(localPrefixes) == 0 { return nil, E.New("missing local address") } - var err error outbound.tunDevice, err = wireguard.NewStackDevice(localPrefixes, uint32(mtu)) if err != nil { return nil, E.Cause(err, "create WireGuard device") diff --git a/docs/config.yaml b/docs/config.yaml index d912eb65..4d0e995e 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -708,12 +708,10 @@ proxies: # socks5 # dialer-proxy: "ss1" # remote-dns-resolve: true # 强制dns远程解析,默认值为false # dns: [ 1.1.1.1, 8.8.8.8 ] # 仅在remote-dns-resolve为true时生效 - # 如果peers不为空,该段落中的allowed-ips不可为空;前面段落的server,port,ip,ipv6,public-key,pre-shared-key均会被忽略,但private-key会被保留且只能在顶层指定 + # 如果peers不为空,该段落中的allowed-ips不可为空;前面段落的server,port,public-key,pre-shared-key均会被忽略,但private-key会被保留且只能在顶层指定 # peers: # - server: 162.159.192.1 # port: 2480 - # ip: 172.16.0.2 - # ipv6: fd01:5ca1:ab1e:80fa:ab85:6eea:213f:f4a5 # public-key: Cr8hWlKvtDt7nrvf+f0brNQQzabAqrjfBvas9pmowjo= # # pre-shared-key: 31aIhAPwktDGpH4JDhA8GNvjFXEf/a6+UaQRyOAiyfM= # allowed-ips: ['0.0.0.0/0'] From e1a5b93cce7088e0c5806823e70425916bedd593 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 20 Mar 2024 12:34:21 +0800 Subject: [PATCH 57/65] chore: rebuild wireguard server address resolve --- adapter/outbound/wireguard.go | 187 ++++++++++++++++++++++------------ go.mod | 2 +- go.sum | 4 +- 3 files changed, 124 insertions(+), 69 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 79803842..3a21c938 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -13,6 +13,7 @@ import ( "strings" "sync" + "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" @@ -37,8 +38,7 @@ type WireGuard struct { device *device.Device tunDevice wireguard.Device dialer proxydialer.SingDialer - startOnce sync.Once - startErr error + init func(ctx context.Context) error resolver *dns.Resolver refP *refProxyAdapter } @@ -141,6 +141,14 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { } runtime.SetFinalizer(outbound, closeWireGuard) + resolv := func(ctx context.Context, address M.Socksaddr) (netip.AddrPort, error) { + if address.Addr.IsValid() { + return address.AddrPort(), nil + } + udpAddr, err := resolveUDPAddrWithPrefer(ctx, "udp", address.String(), outbound.prefer) + return udpAddr.AddrPort(), err + } + var reserved [3]uint8 if len(option.Reserved) > 0 { if len(option.Reserved) != 3 { @@ -158,7 +166,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { connectAddr = option.Addr() } } - outbound.bind = wireguard.NewClientBind(context.Background(), wgSingErrorHandler{outbound.Name()}, outbound.dialer, isConnect, connectAddr, reserved) + outbound.bind = wireguard.NewClientBind(context.Background(), wgSingErrorHandler{outbound.Name()}, outbound.dialer, isConnect, connectAddr.AddrPort(), reserved) localPrefixes, err := option.Prefixes() if err != nil { @@ -173,84 +181,145 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { } privateKey = hex.EncodeToString(bytes) } - ipcConf := "private_key=" + privateKey - if peersLen := len(option.Peers); peersLen > 0 { + + if len(option.Peers) > 0 { for i, peer := range option.Peers { - var peerPublicKey, preSharedKey string - { - bytes, err := base64.StdEncoding.DecodeString(peer.PublicKey) - if err != nil { - return nil, E.Cause(err, "decode public key for peer ", i) - } - peerPublicKey = hex.EncodeToString(bytes) + bytes, err := base64.StdEncoding.DecodeString(peer.PublicKey) + if err != nil { + return nil, E.Cause(err, "decode public key for peer ", i) } + peer.PublicKey = hex.EncodeToString(bytes) + if peer.PreSharedKey != "" { bytes, err := base64.StdEncoding.DecodeString(peer.PreSharedKey) if err != nil { return nil, E.Cause(err, "decode pre shared key for peer ", i) } - preSharedKey = hex.EncodeToString(bytes) - } - destination := peer.Addr() - ipcConf += "\npublic_key=" + peerPublicKey - ipcConf += "\nendpoint=" + destination.String() - if preSharedKey != "" { - ipcConf += "\npreshared_key=" + preSharedKey + peer.PreSharedKey = hex.EncodeToString(bytes) } + if len(peer.AllowedIPs) == 0 { return nil, E.New("missing allowed_ips for peer ", i) } - for _, allowedIP := range peer.AllowedIPs { - ipcConf += "\nallowed_ip=" + allowedIP - } + if len(peer.Reserved) > 0 { if len(peer.Reserved) != 3 { return nil, E.New("invalid reserved value for peer ", i, ", required 3 bytes, got ", len(peer.Reserved)) } - copy(reserved[:], option.Reserved) - outbound.bind.SetReservedForEndpoint(destination, reserved) } } } else { - var peerPublicKey, preSharedKey string { bytes, err := base64.StdEncoding.DecodeString(option.PublicKey) if err != nil { return nil, E.Cause(err, "decode peer public key") } - peerPublicKey = hex.EncodeToString(bytes) + option.PublicKey = hex.EncodeToString(bytes) } if option.PreSharedKey != "" { bytes, err := base64.StdEncoding.DecodeString(option.PreSharedKey) if err != nil { return nil, E.Cause(err, "decode pre shared key") } - preSharedKey = hex.EncodeToString(bytes) - } - ipcConf += "\npublic_key=" + peerPublicKey - ipcConf += "\nendpoint=" + connectAddr.String() - if preSharedKey != "" { - ipcConf += "\npreshared_key=" + preSharedKey - } - var has4, has6 bool - for _, address := range localPrefixes { - if address.Addr().Is4() { - has4 = true - } else { - has6 = true - } - } - if has4 { - ipcConf += "\nallowed_ip=0.0.0.0/0" - } - if has6 { - ipcConf += "\nallowed_ip=::/0" + option.PreSharedKey = hex.EncodeToString(bytes) } } - if option.PersistentKeepalive != 0 { - ipcConf += fmt.Sprintf("\npersistent_keepalive_interval=%d", option.PersistentKeepalive) + var ( + initOk atomic.Bool + initMutex sync.Mutex + initErr error + ) + + outbound.init = func(ctx context.Context) error { + if initOk.Load() { + return nil + } + initMutex.Lock() + defer initMutex.Unlock() + // double check like sync.Once + if initOk.Load() { + return nil + } + if initErr != nil { + return initErr + } + + outbound.bind.ResetReservedForEndpoint() + ipcConf := "private_key=" + privateKey + if len(option.Peers) > 0 { + for i, peer := range option.Peers { + destination, err := resolv(ctx, peer.Addr()) + if err != nil { + // !!! do not set initErr here !!! + // let us can retry domain resolve in next time + return E.Cause(err, "resolve endpoint domain for peer ", i) + } + ipcConf += "\npublic_key=" + peer.PublicKey + ipcConf += "\nendpoint=" + destination.String() + if peer.PreSharedKey != "" { + ipcConf += "\npreshared_key=" + peer.PreSharedKey + } + for _, allowedIP := range peer.AllowedIPs { + ipcConf += "\nallowed_ip=" + allowedIP + } + if len(peer.Reserved) > 0 { + copy(reserved[:], option.Reserved) + outbound.bind.SetReservedForEndpoint(destination, reserved) + } + } + } else { + ipcConf += "\npublic_key=" + option.PublicKey + destination, err := resolv(ctx, connectAddr) + if err != nil { + // !!! do not set initErr here !!! + // let us can retry domain resolve in next time + return E.Cause(err, "resolve endpoint domain") + } + outbound.bind.SetConnectAddr(destination) + ipcConf += "\nendpoint=" + destination.String() + if option.PreSharedKey != "" { + ipcConf += "\npreshared_key=" + option.PreSharedKey + } + var has4, has6 bool + for _, address := range localPrefixes { + if address.Addr().Is4() { + has4 = true + } else { + has6 = true + } + } + if has4 { + ipcConf += "\nallowed_ip=0.0.0.0/0" + } + if has6 { + ipcConf += "\nallowed_ip=::/0" + } + } + + if option.PersistentKeepalive != 0 { + ipcConf += fmt.Sprintf("\npersistent_keepalive_interval=%d", option.PersistentKeepalive) + } + + if debug.Enabled { + log.SingLogger.Trace(fmt.Sprintf("[WG](%s) created wireguard ipc conf: \n %s", option.Name, ipcConf)) + } + err = outbound.device.IpcSet(ipcConf) + if err != nil { + initErr = E.Cause(err, "setup wireguard") + return initErr + } + + err = outbound.tunDevice.Start() + if err != nil { + initErr = err + return initErr + } + + initOk.Store(true) + return nil } + mtu := option.MTU if mtu == 0 { mtu = 1408 @@ -270,14 +339,6 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { log.SingLogger.Error(fmt.Sprintf("[WG](%s) %s", option.Name, fmt.Sprintf(format, args...))) }, }, option.Workers) - if debug.Enabled { - log.SingLogger.Trace(fmt.Sprintf("[WG](%s) created wireguard ipc conf: \n %s", option.Name, ipcConf)) - } - err = outbound.device.IpcSet(ipcConf) - if err != nil { - return nil, E.Cause(err, "setup wireguard") - } - //err = outbound.tunDevice.Start() var has6 bool for _, address := range localPrefixes { @@ -317,11 +378,8 @@ func (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata, opts options := w.Base.DialOptions(opts...) w.dialer.SetDialer(dialer.NewDialer(options...)) var conn net.Conn - w.startOnce.Do(func() { - w.startErr = w.tunDevice.Start() - }) - if w.startErr != nil { - return nil, w.startErr + if err = w.init(ctx); err != nil { + return nil, err } if !metadata.Resolved() || w.resolver != nil { r := resolver.DefaultResolver @@ -349,11 +407,8 @@ func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadat options := w.Base.DialOptions(opts...) w.dialer.SetDialer(dialer.NewDialer(options...)) var pc net.PacketConn - w.startOnce.Do(func() { - w.startErr = w.tunDevice.Start() - }) - if w.startErr != nil { - return nil, w.startErr + if err = w.init(ctx); err != nil { + return nil, err } if err != nil { return nil, err diff --git a/go.mod b/go.mod index c86ce9ce..7ff43a9f 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/metacubex/sing-shadowsocks2 v0.2.0 github.com/metacubex/sing-tun v0.2.1-0.20240320004934-5d2b35447bfd github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f - github.com/metacubex/sing-wireguard v0.0.0-20240320005523-c46db1e2d1be + github.com/metacubex/sing-wireguard v0.0.0-20240320043244-d6a8de454284 github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 github.com/miekg/dns v1.1.58 github.com/mroth/weightedrand/v2 v2.1.0 diff --git a/go.sum b/go.sum index acb0b28a..8f557996 100644 --- a/go.sum +++ b/go.sum @@ -118,8 +118,8 @@ github.com/metacubex/sing-tun v0.2.1-0.20240320004934-5d2b35447bfd h1:NgLb6Lvr8Z github.com/metacubex/sing-tun v0.2.1-0.20240320004934-5d2b35447bfd/go.mod h1:GfLZG/QgGpW9+BPjltzONrL5vVms86TWqmZ23J68ISc= 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-20240320005523-c46db1e2d1be h1:xZHAKZPMdVIwk8zCbmL6vi87xC4VRKWhNLRoO3TdRwA= -github.com/metacubex/sing-wireguard v0.0.0-20240320005523-c46db1e2d1be/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo= +github.com/metacubex/sing-wireguard v0.0.0-20240320043244-d6a8de454284 h1:aort+t6Hb+umsOFODT/P5fzTWr/4Bypp70jXUHhryR8= +github.com/metacubex/sing-wireguard v0.0.0-20240320043244-d6a8de454284/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo= github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 h1:as/aO/fM8nv4W4pOr9EETP6kV/Oaujk3fUNyQSJK61c= github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= From 2e94531c72eca564ddbec268bf7760cfbca44428 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 20 Mar 2024 18:07:13 +0800 Subject: [PATCH 58/65] Revert "fix hysteria faketcp lookback in TUN mode (#601)" This reverts commit fdaa6a22a4d14ccb08a5173128e1c47585678627. --- component/dialer/bind.go | 3 ++- component/iface/iface.go | 10 ---------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/component/dialer/bind.go b/component/dialer/bind.go index 72df8c72..9b6471a3 100644 --- a/component/dialer/bind.go +++ b/component/dialer/bind.go @@ -14,6 +14,7 @@ func LookupLocalAddrFromIfaceName(ifaceName string, network string, destination if err != nil { return nil, err } + destination = destination.Unmap() var addr netip.Prefix switch network { @@ -23,7 +24,7 @@ func LookupLocalAddrFromIfaceName(ifaceName string, network string, destination addr, err = ifaceObj.PickIPv6Addr(destination) default: if destination.IsValid() { - if destination.Is4() || destination.Is4In6() { + if destination.Is4() { addr, err = ifaceObj.PickIPv4Addr(destination) } else { addr, err = ifaceObj.PickIPv6Addr(destination) diff --git a/component/iface/iface.go b/component/iface/iface.go index bf186165..dd932b46 100644 --- a/component/iface/iface.go +++ b/component/iface/iface.go @@ -4,7 +4,6 @@ import ( "errors" "net" "net/netip" - "strings" "time" "github.com/metacubex/mihomo/common/singledo" @@ -38,21 +37,12 @@ func ResolveInterface(name string) (*Interface, error) { if err != nil { continue } - // if not available device like Meta, dummy0, docker0, etc. - if (iface.Flags&net.FlagMulticast == 0) || (iface.Flags&net.FlagPointToPoint != 0) || (iface.Flags&net.FlagRunning == 0) { - continue - } ipNets := make([]netip.Prefix, 0, len(addrs)) for _, addr := range addrs { ipNet := addr.(*net.IPNet) ip, _ := netip.AddrFromSlice(ipNet.IP) - //unavailable IPv6 Address - if ip.Is6() && strings.HasPrefix(ip.String(), "fe80") { - continue - } - ones, bits := ipNet.Mask.Size() if bits == 32 { ip = ip.Unmap() From 284b01ca389d2228800c7411a471c50ccbbc1294 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 21 Mar 2024 12:23:45 +0800 Subject: [PATCH 59/65] fix: wireguard client bind --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7ff43a9f..c70aca97 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/metacubex/sing-shadowsocks2 v0.2.0 github.com/metacubex/sing-tun v0.2.1-0.20240320004934-5d2b35447bfd github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f - github.com/metacubex/sing-wireguard v0.0.0-20240320043244-d6a8de454284 + github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 github.com/miekg/dns v1.1.58 github.com/mroth/weightedrand/v2 v2.1.0 diff --git a/go.sum b/go.sum index 8f557996..7422a062 100644 --- a/go.sum +++ b/go.sum @@ -118,8 +118,8 @@ github.com/metacubex/sing-tun v0.2.1-0.20240320004934-5d2b35447bfd h1:NgLb6Lvr8Z github.com/metacubex/sing-tun v0.2.1-0.20240320004934-5d2b35447bfd/go.mod h1:GfLZG/QgGpW9+BPjltzONrL5vVms86TWqmZ23J68ISc= 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-20240320043244-d6a8de454284 h1:aort+t6Hb+umsOFODT/P5fzTWr/4Bypp70jXUHhryR8= -github.com/metacubex/sing-wireguard v0.0.0-20240320043244-d6a8de454284/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo= +github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 h1:AGyIB55UfQm/0ZH0HtQO9u3l//yjtHUpjeRjjPGfGRI= +github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63/go.mod h1:uY+BYb0UEknLrqvbGcwi9i++KgrKxsurysgI6G1Pveo= github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 h1:as/aO/fM8nv4W4pOr9EETP6kV/Oaujk3fUNyQSJK61c= github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= From 9c08e936f971e4261e854d7bf039e28ab3eb9593 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 22 Mar 2024 00:33:38 +0800 Subject: [PATCH 60/65] fix: unmap 4in6 ip in wireguard --- adapter/outbound/wireguard.go | 7 ++++++- dns/resolver.go | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 3a21c938..fe1f69fa 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -146,7 +146,12 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { return address.AddrPort(), nil } udpAddr, err := resolveUDPAddrWithPrefer(ctx, "udp", address.String(), outbound.prefer) - return udpAddr.AddrPort(), err + if err != nil { + return netip.AddrPort{}, err + } + // net.ResolveUDPAddr maybe return 4in6 address, so unmap at here + addrPort := udpAddr.AddrPort() + return netip.AddrPortFrom(addrPort.Addr().Unmap(), addrPort.Port()), nil } var reserved [3]uint8 diff --git a/dns/resolver.go b/dns/resolver.go index 8ea68ed7..08de69ad 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -324,7 +324,7 @@ func (r *Resolver) ipExchange(ctx context.Context, m *D.Msg) (msg *D.Msg, err er func (r *Resolver) lookupIP(ctx context.Context, host string, dnsType uint16) (ips []netip.Addr, err error) { ip, err := netip.ParseAddr(host) if err == nil { - isIPv4 := ip.Is4() + isIPv4 := ip.Is4() || ip.Is4In6() if dnsType == D.TypeAAAA && !isIPv4 { return []netip.Addr{ip}, nil } else if dnsType == D.TypeA && isIPv4 { From d56a439a741ec8762409598bc927390cdf67c5fa Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 23 Mar 2024 22:30:19 +0800 Subject: [PATCH 61/65] fix: dns truncate not work --- dns/client.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/dns/client.go b/dns/client.go index fc76c124..3b4efed1 100644 --- a/dns/client.go +++ b/dns/client.go @@ -78,7 +78,9 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) options = append(options, dialer.WithInterface(c.iface)) } - conn, err := getDialHandler(c.r, c.proxyAdapter, c.proxyName, options...)(ctx, network, net.JoinHostPort(ip.String(), c.port)) + dialHandler := getDialHandler(c.r, c.proxyAdapter, c.proxyName, options...) + addr := net.JoinHostPort(ip.String(), c.port) + conn, err := dialHandler(ctx, network, addr) if err != nil { return nil, err } @@ -111,7 +113,16 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) if msg != nil && msg.Truncated && c.Client.Net == "" { tcpClient := *c.Client // copy a client tcpClient.Net = "tcp" + network = "tcp" log.Debugln("[DNS] Truncated reply from %s:%s for %s over UDP, retrying over TCP", c.host, c.port, m.Question[0].String()) + dConn.Conn, err = dialHandler(ctx, network, addr) + if err != nil { + ch <- result{msg, err} + return + } + defer func() { + _ = conn.Close() + }() msg, _, err = tcpClient.ExchangeWithConn(m, dConn) } From d2ae94f20b70a23f82fc74d529bbe4ef3fd43e1d Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 24 Mar 2024 21:24:50 +0800 Subject: [PATCH 62/65] fix: iface panic https://github.com/MetaCubeX/mihomo/issues/1130 --- component/iface/iface.go | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/component/iface/iface.go b/component/iface/iface.go index dd932b46..2fd36861 100644 --- a/component/iface/iface.go +++ b/component/iface/iface.go @@ -40,16 +40,25 @@ func ResolveInterface(name string) (*Interface, error) { ipNets := make([]netip.Prefix, 0, len(addrs)) for _, addr := range addrs { - ipNet := addr.(*net.IPNet) - ip, _ := netip.AddrFromSlice(ipNet.IP) - - ones, bits := ipNet.Mask.Size() - if bits == 32 { + var pf netip.Prefix + switch addr.(type) { + case *net.IPNet: + ipNet := addr.(*net.IPNet) + ip, _ := netip.AddrFromSlice(ipNet.IP) + ones, bits := ipNet.Mask.Size() + if bits == 32 { + ip = ip.Unmap() + } + pf = netip.PrefixFrom(ip, ones) + case *net.IPAddr: + ipNet := addr.(*net.IPAddr) + ip, _ := netip.AddrFromSlice(ipNet.IP) ip = ip.Unmap() + pf = netip.PrefixFrom(ip, ip.BitLen()) + } + if pf.IsValid() { + ipNets = append(ipNets, pf) } - - pf := netip.PrefixFrom(ip, ones) - ipNets = append(ipNets, pf) } r[iface.Name] = &Interface{ From 5af7f4e847ede794c4db4cbaa0964cba0eb8e5dc Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 24 Mar 2024 21:31:52 +0800 Subject: [PATCH 63/65] chore: allow config `table-index` for tun https://github.com/MetaCubeX/mihomo/issues/1128 --- config/config.go | 2 ++ hub/route/configs.go | 4 ++++ listener/config/tun.go | 1 + listener/inbound/tun.go | 2 ++ listener/listener.go | 3 ++- listener/sing_tun/server.go | 6 +++++- 6 files changed, 16 insertions(+), 2 deletions(-) diff --git a/config/config.go b/config/config.go index ca866491..ca179ed0 100644 --- a/config/config.go +++ b/config/config.go @@ -265,6 +265,7 @@ type RawTun struct { EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint_independent_nat,omitempty"` UDPTimeout int64 `yaml:"udp-timeout" json:"udp_timeout,omitempty"` FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"` + TableIndex int `yaml:"table-index" json:"table-index"` } type RawTuicServer struct { @@ -1448,6 +1449,7 @@ func parseTun(rawTun RawTun, general *General) error { EndpointIndependentNat: rawTun.EndpointIndependentNat, UDPTimeout: rawTun.UDPTimeout, FileDescriptor: rawTun.FileDescriptor, + TableIndex: rawTun.TableIndex, } return nil diff --git a/hub/route/configs.go b/hub/route/configs.go index ec0b464c..653e4351 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -91,6 +91,7 @@ type tunSchema struct { EndpointIndependentNat *bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"` UDPTimeout *int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"` FileDescriptor *int `yaml:"file-descriptor" json:"file-descriptor"` + TableIndex *int `yaml:"table-index" json:"table-index"` } type tuicServerSchema struct { @@ -209,6 +210,9 @@ func pointerOrDefaultTun(p *tunSchema, def LC.Tun) LC.Tun { if p.FileDescriptor != nil { def.FileDescriptor = *p.FileDescriptor } + if p.TableIndex != nil { + def.TableIndex = *p.TableIndex + } } return def } diff --git a/listener/config/tun.go b/listener/config/tun.go index 1772c6f5..7467e4a6 100644 --- a/listener/config/tun.go +++ b/listener/config/tun.go @@ -49,4 +49,5 @@ type Tun struct { EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"` UDPTimeout int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"` FileDescriptor int `yaml:"file-descriptor" json:"file-descriptor"` + TableIndex int `yaml:"table-index" json:"table-index"` } diff --git a/listener/inbound/tun.go b/listener/inbound/tun.go index a1fdebfa..51747c46 100644 --- a/listener/inbound/tun.go +++ b/listener/inbound/tun.go @@ -40,6 +40,7 @@ type TunOption struct { EndpointIndependentNat bool `inbound:"endpoint_independent_nat,omitempty"` UDPTimeout int64 `inbound:"udp_timeout,omitempty"` FileDescriptor int `inbound:"file-descriptor,omitempty"` + TableIndex int `inbound:"table-index,omitempty"` } func (o TunOption) Equal(config C.InboundConfig) bool { @@ -118,6 +119,7 @@ func NewTun(options *TunOption) (*Tun, error) { EndpointIndependentNat: options.EndpointIndependentNat, UDPTimeout: options.UDPTimeout, FileDescriptor: options.FileDescriptor, + TableIndex: options.TableIndex, }, }, nil } diff --git a/listener/listener.go b/listener/listener.go index ac602971..e3506188 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -823,7 +823,8 @@ func hasTunConfigChange(tunConf *LC.Tun) bool { LastTunConf.StrictRoute != tunConf.StrictRoute || LastTunConf.EndpointIndependentNat != tunConf.EndpointIndependentNat || LastTunConf.UDPTimeout != tunConf.UDPTimeout || - LastTunConf.FileDescriptor != tunConf.FileDescriptor { + LastTunConf.FileDescriptor != tunConf.FileDescriptor || + LastTunConf.TableIndex != tunConf.TableIndex { return true } diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index cc26d37d..96ec1573 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -112,6 +112,10 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis } else { udpTimeout = int64(sing.UDPTimeout.Seconds()) } + tableIndex := options.TableIndex + if tableIndex == 0 { + tableIndex = 2022 + } includeUID := uidToRange(options.IncludeUID) if len(options.IncludeUIDRange) > 0 { var err error @@ -225,7 +229,7 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis ExcludePackage: options.ExcludePackage, FileDescriptor: options.FileDescriptor, InterfaceMonitor: defaultInterfaceMonitor, - TableIndex: 2022, + TableIndex: tableIndex, } err = l.buildAndroidRules(&tunOptions) From 288899a47318902802800230d5e35ea7416b52ea Mon Sep 17 00:00:00 2001 From: bobo liu <7552030+fakeboboliu@users.noreply.github.com> Date: Sun, 24 Mar 2024 21:41:05 +0800 Subject: [PATCH 64/65] chore: stylish d2ae94f2 (#1132) --- component/iface/iface.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/component/iface/iface.go b/component/iface/iface.go index 2fd36861..1d0219df 100644 --- a/component/iface/iface.go +++ b/component/iface/iface.go @@ -41,9 +41,8 @@ func ResolveInterface(name string) (*Interface, error) { ipNets := make([]netip.Prefix, 0, len(addrs)) for _, addr := range addrs { var pf netip.Prefix - switch addr.(type) { + switch ipNet := addr.(type) { case *net.IPNet: - ipNet := addr.(*net.IPNet) ip, _ := netip.AddrFromSlice(ipNet.IP) ones, bits := ipNet.Mask.Size() if bits == 32 { @@ -51,7 +50,6 @@ func ResolveInterface(name string) (*Interface, error) { } pf = netip.PrefixFrom(ip, ones) case *net.IPAddr: - ipNet := addr.(*net.IPAddr) ip, _ := netip.AddrFromSlice(ipNet.IP) ip = ip.Unmap() pf = netip.PrefixFrom(ip, ip.BitLen()) From 0b4662e4b7f52f631112d16a69b81e1a49c8fe42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=90=BD=E5=BF=83?= <33619903+Luoxin@users.noreply.github.com> Date: Tue, 26 Mar 2024 14:19:33 +0800 Subject: [PATCH 65/65] fixed: invalid argument to Intn (#1133) --- transport/vmess/http.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/transport/vmess/http.go b/transport/vmess/http.go index 6da9759e..b023fee4 100644 --- a/transport/vmess/http.go +++ b/transport/vmess/http.go @@ -3,6 +3,7 @@ package vmess import ( "bufio" "bytes" + "errors" "fmt" "net" "net/http" @@ -54,6 +55,10 @@ func (hc *httpConn) Write(b []byte) (int, error) { return hc.Conn.Write(b) } + if len(hc.cfg.Path) == 0 { + return -1, errors.New("path is empty") + } + path := hc.cfg.Path[fastrand.Intn(len(hc.cfg.Path))] host := hc.cfg.Host if header := hc.cfg.Headers["Host"]; len(header) != 0 {