From 4243a7428478b786bde56421d57cc15d857c3cfc Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Sun, 19 May 2024 17:30:39 +0800 Subject: [PATCH 01/38] chore: auto update geo --- component/updater/update_geo.go | 25 +++++++++------ hub/route/configs.go | 54 ++++++++++----------------------- main.go | 26 +++++++--------- 3 files changed, 42 insertions(+), 63 deletions(-) diff --git a/component/updater/update_geo.go b/component/updater/update_geo.go index 4bb57e06..b07cd315 100644 --- a/component/updater/update_geo.go +++ b/component/updater/update_geo.go @@ -98,11 +98,13 @@ func updateGeoDatabases() error { return nil } -func UpdateGeoDatabases(updateNotification chan struct{}) error { +var ErrGetDatabaseUpdateSkip = errors.New("GEO database is updating, skip") + +func UpdateGeoDatabases() error { log.Infoln("[GEO] Start updating GEO database") if UpdatingGeo.Load() { - return errors.New("GEO database is updating, skip") + return ErrGetDatabaseUpdateSkip } UpdatingGeo.Store(true) @@ -115,7 +117,6 @@ func UpdateGeoDatabases(updateNotification chan struct{}) error { return err } - updateNotification <- struct{}{} return nil } @@ -136,17 +137,16 @@ func getUpdateTime() (err error, time time.Time) { return nil, fileInfo.ModTime() } -func RegisterGeoUpdater(updateNotification chan struct{}) { +func RegisterGeoUpdater(onSuccess func()) { if C.GeoUpdateInterval <= 0 { log.Errorln("[GEO] Invalid update interval: %d", C.GeoUpdateInterval) return } - ticker := time.NewTicker(time.Duration(C.GeoUpdateInterval) * time.Hour) - defer ticker.Stop() - - log.Infoln("[GEO] update GEO database every %d hours", C.GeoUpdateInterval) go func() { + ticker := time.NewTicker(time.Duration(C.GeoUpdateInterval) * time.Hour) + defer ticker.Stop() + err, lastUpdate := getUpdateTime() if err != nil { log.Errorln("[GEO] Get GEO database update time error: %s", err.Error()) @@ -156,15 +156,20 @@ func RegisterGeoUpdater(updateNotification chan struct{}) { log.Infoln("[GEO] last update time %s", lastUpdate) if lastUpdate.Add(time.Duration(C.GeoUpdateInterval) * time.Hour).Before(time.Now()) { log.Infoln("[GEO] Database has not been updated for %v, update now", time.Duration(C.GeoUpdateInterval)*time.Hour) - if err := UpdateGeoDatabases(updateNotification); err != nil { + if err := UpdateGeoDatabases(); err != nil { log.Errorln("[GEO] Failed to update GEO database: %s", err.Error()) return + } else { + onSuccess() } } for range ticker.C { - if err := UpdateGeoDatabases(updateNotification); err != nil { + log.Infoln("[GEO] updating database every %d hours", C.GeoUpdateInterval) + if err := UpdateGeoDatabases(); err != nil { log.Errorln("[GEO] Failed to update GEO database: %s", err.Error()) + } else { + onSuccess() } } }() diff --git a/hub/route/configs.go b/hub/route/configs.go index 35977885..f2cf298a 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -364,47 +364,25 @@ func updateConfigs(w http.ResponseWriter, r *http.Request) { } func updateGeoDatabases(w http.ResponseWriter, r *http.Request) { - updateNotification := make(chan struct{}) - errorChannel := make(chan error, 1) - done := make(chan struct{}) - defer func() { - close(updateNotification) - close(errorChannel) - }() + err := updater.UpdateGeoDatabases() + if err != nil { + log.Errorln("[REST-API] update GEO databases failed: %v", err) + render.Status(r, http.StatusInternalServerError) + render.JSON(w, r, newError(err.Error())) + return + } - go func() { - defer close(done) - for { - select { - case <-updateNotification: - cfg, err := executor.ParseWithPath(C.Path.Config()) - if err != nil { - log.Errorln("[REST-API] update GEO databases failed: %v", err) - render.Status(r, http.StatusInternalServerError) - render.JSON(w, r, newError("Error parsing configuration")) - return - } + cfg, err := executor.ParseWithPath(C.Path.Config()) + if err != nil { + log.Errorln("[REST-API] update GEO databases failed: %v", err) + render.Status(r, http.StatusInternalServerError) + render.JSON(w, r, newError("Error parsing configuration")) + return + } - log.Warnln("[REST-API] update GEO databases success, applying config") - executor.ApplyConfig(cfg, false) - return - case err := <-errorChannel: - log.Errorln("[REST-API] update GEO databases failed: %v", err) - render.Status(r, http.StatusInternalServerError) - render.JSON(w, r, err.Error()) - return - } - } - }() + log.Warnln("[GEO] update GEO databases success, applying config") - go func() { - err := updater.UpdateGeoDatabases(updateNotification) - if err != nil { - errorChannel <- err - } - }() - - <-done + executor.ApplyConfig(cfg, false) render.NoContent(w, r) } diff --git a/main.go b/main.go index 081a7eb5..61f1d683 100644 --- a/main.go +++ b/main.go @@ -113,23 +113,19 @@ func main() { } if C.GeoAutoUpdate { - updateNotification := make(chan struct{}) - go updater.RegisterGeoUpdater(updateNotification) - - go func() { - for range updateNotification { - cfg, err := executor.ParseWithPath(C.Path.Config()) - if err != nil { - log.Errorln("[GEO] update GEO databases failed: %v", err) - return - } - - log.Warnln("[GEO] update GEO databases success, applying config") - - executor.ApplyConfig(cfg, false) + updater.RegisterGeoUpdater(func() { + cfg, err := executor.ParseWithPath(C.Path.Config()) + if err != nil { + log.Errorln("[GEO] update GEO databases failed: %v", err) + return } - }() + + log.Warnln("[GEO] update GEO databases success, applying config") + + executor.ApplyConfig(cfg, false) + }) } + defer executor.Shutdown() termSign := make(chan os.Signal, 1) From c504985b99c9e437be01d842b6589fc6095b708b Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sun, 19 May 2024 19:35:12 +0800 Subject: [PATCH 02/38] chore: Adjust sniff logs --- component/sniffer/dispatcher.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/component/sniffer/dispatcher.go b/component/sniffer/dispatcher.go index 96e9272c..97bf1629 100644 --- a/component/sniffer/dispatcher.go +++ b/component/sniffer/dispatcher.go @@ -116,14 +116,13 @@ func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata } func (sd *SnifferDispatcher) replaceDomain(metadata *C.Metadata, host string, overrideDest bool) { - // show log early, since the following code may mutate `metadata.Host` - log.Debugln("[Sniffer] Sniff %s [%s]-->[%s] success, replace domain [%s]-->[%s]", - metadata.NetWork, - metadata.SourceDetail(), - metadata.RemoteAddress(), - metadata.Host, host) metadata.SniffHost = host if overrideDest { + log.Debugln("[Sniffer] Sniff %s [%s]-->[%s] success, replace domain [%s]-->[%s]", + metadata.NetWork, + metadata.SourceDetail(), + metadata.RemoteAddress(), + metadata.Host, host) metadata.Host = host } metadata.DNSMode = C.DNSNormal From bd43eca09d4ba82ede087177ade04a91fb0bacb2 Mon Sep 17 00:00:00 2001 From: Larvan2 <78135608+Larvan2@users.noreply.github.com> Date: Mon, 20 May 2024 19:46:15 +0800 Subject: [PATCH 03/38] ci:docker tags --- .github/workflows/build.yml | 65 +++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 84536a54..42023755 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ on: branches: - Alpha concurrency: - group: ${{ github.workflow }}-${{ github.ref }} + group: "${{ github.workflow }}-${{ github.ref }}" cancel-in-progress: true env: @@ -270,7 +270,7 @@ jobs: - name: Archive production artifacts uses: actions/upload-artifact@v4 with: - name: ${{ matrix.jobs.goos }}-${{ matrix.jobs.output }} + name: "${{ matrix.jobs.goos }}-${{ matrix.jobs.output }}" path: | mihomo*.gz mihomo*.deb @@ -415,35 +415,35 @@ jobs: uses: docker/setup-buildx-action@v3 with: version: latest - - - name: Set Docker tags and labels based on trigger - id: set-meta - run: | - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - echo "tags=${{ github.event.inputs.version }}" >> $GITHUB_ENV - echo "labels=org.opencontainers.image.version=${{ github.event.inputs.version }}" >> $GITHUB_ENV - else - echo "tags=${{ steps.meta.outputs.tags }}" >> $GITHUB_ENV - echo "labels=${{ steps.meta.outputs.labels }}" >> $GITHUB_ENV - fi - - + # Extract metadata (tags, labels) for Docker # https://github.com/docker/metadata-action - name: Extract Docker metadata - id: meta + if: ${{ github.event_name != 'workflow_dispatch' }} + id: meta_alpha uses: docker/metadata-action@v5 with: - images: ${{ env.REGISTRY }}/${{ github.repository }} - tags: ${{ env.tags }} - labels: ${{ env.labels }} - - + images: '${{ env.REGISTRY }}/${{ github.repository }}' + + # Extract metadata (tags, labels) for Docker + # https://github.com/docker/metadata-action + - name: Extract Docker metadata + if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' }} + id: meta_release + uses: docker/metadata-action@v5 + with: + images: '${{ env.REGISTRY }}/${{ github.repository }}' + tags: | + ${{ github.event.inputs.version }} + flavor: | + latest=true + labels: org.opencontainers.image.version=${{ github.event.inputs.version }} + - name: Show files run: | ls . ls bin/ - + - name: login to docker REGISTRY uses: docker/login-action@v3 with: @@ -454,7 +454,7 @@ jobs: # Build and push Docker image with Buildx (don't push on PR) # https://github.com/docker/build-push-action - name: Build and push Docker image - id: build-and-push + if: ${{ github.event_name != 'workflow_dispatch' }} uses: docker/build-push-action@v5 with: context: . @@ -465,5 +465,20 @@ jobs: linux/amd64 linux/arm64 linux/arm/v7 - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file + tags: ${{ steps.meta_alpha.outputs.tags }} + labels: ${{ steps.meta_alpha.outputs.labels }} + + - name: Build and push Docker image + if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' }} + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + push: ${{ github.event_name != 'pull_request' }} + platforms: | + linux/386 + linux/amd64 + linux/arm64 + linux/arm/v7 + tags: ${{ steps.meta_release.outputs.tags }} + labels: ${{ steps.meta_release.outputs.labels }} \ No newline at end of file From 3195c678c7b2118f4ec56a9af352b5722c4cdf5c Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 21 May 2024 00:47:59 +0800 Subject: [PATCH 04/38] chore: update quic-go to 0.44.0 --- go.mod | 2 +- go.sum | 4 ++-- transport/tuic/common/congestion.go | 8 ++++---- transport/tuic/congestion/bbr_sender.go | 21 +++++---------------- transport/tuic/congestion/cubic.go | 2 +- transport/tuic/congestion_v2/bbr_sender.go | 18 ++++-------------- transport/tuic/congestion_v2/pacer.go | 4 ++-- 7 files changed, 19 insertions(+), 40 deletions(-) diff --git a/go.mod b/go.mod index d9ed1228..ad2492ff 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.43.2-0.20240518033621-2c3d14c6b38e + github.com/metacubex/quic-go v0.44.1-0.20240521004242-fcd70d587e22 github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 diff --git a/go.sum b/go.sum index fb14d715..0b4b8d3c 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-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.43.2-0.20240518033621-2c3d14c6b38e h1:Nzwe08FNIJpExWpy9iXkG336dN/8nJqn69yijB7vJ8g= -github.com/metacubex/quic-go v0.43.2-0.20240518033621-2c3d14c6b38e/go.mod h1:uXHODgJFUfUnkkCMWLd5Er6L5QY/LFRZb9LD5jyyhsk= +github.com/metacubex/quic-go v0.44.1-0.20240521004242-fcd70d587e22 h1:hsQ0b2A509b6ubnLtLOcUgZ8vOb+d/667zVEJ1T2fao= +github.com/metacubex/quic-go v0.44.1-0.20240521004242-fcd70d587e22/go.mod h1:88wAATpevav4xdy5N8oejQ2cbbI6EcLYEklFeo+qywA= github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1 h1:7hDHLTmjgtRoAp59STwPQpe5Pinwi4cWex+FB3Ohvco= github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 h1:Wr4g1HCb5Z/QIFwFiVNjO2qL+dRu25+Mdn9xtAZZ+ew= diff --git a/transport/tuic/common/congestion.go b/transport/tuic/common/congestion.go index 485e2e6a..a0e2be68 100644 --- a/transport/tuic/common/congestion.go +++ b/transport/tuic/common/congestion.go @@ -22,7 +22,7 @@ func SetCongestionController(quicConn quic.Connection, cc string, cwnd int) { quicConn.SetCongestionControl( congestion.NewCubicSender( congestion.DefaultClock{}, - congestion.GetInitialPacketSize(quicConn.RemoteAddr()), + congestion.GetInitialPacketSize(quicConn), false, ), ) @@ -30,7 +30,7 @@ func SetCongestionController(quicConn quic.Connection, cc string, cwnd int) { quicConn.SetCongestionControl( congestion.NewCubicSender( congestion.DefaultClock{}, - congestion.GetInitialPacketSize(quicConn.RemoteAddr()), + congestion.GetInitialPacketSize(quicConn), true, ), ) @@ -38,7 +38,7 @@ func SetCongestionController(quicConn quic.Connection, cc string, cwnd int) { quicConn.SetCongestionControl( congestion.NewBBRSender( congestion.DefaultClock{}, - congestion.GetInitialPacketSize(quicConn.RemoteAddr()), + congestion.GetInitialPacketSize(quicConn), c.ByteCount(cwnd)*congestion.InitialMaxDatagramSize, congestion.DefaultBBRMaxCongestionWindow*congestion.InitialMaxDatagramSize, ), @@ -49,7 +49,7 @@ func SetCongestionController(quicConn quic.Connection, cc string, cwnd int) { quicConn.SetCongestionControl( congestionv2.NewBbrSender( congestionv2.DefaultClock{}, - congestionv2.GetInitialPacketSize(quicConn.RemoteAddr()), + congestionv2.GetInitialPacketSize(quicConn), c.ByteCount(cwnd), ), ) diff --git a/transport/tuic/congestion/bbr_sender.go b/transport/tuic/congestion/bbr_sender.go index 8c18c616..6cea9355 100644 --- a/transport/tuic/congestion/bbr_sender.go +++ b/transport/tuic/congestion/bbr_sender.go @@ -5,34 +5,23 @@ package congestion import ( "fmt" "math" - "net" "time" + "github.com/metacubex/quic-go" "github.com/metacubex/quic-go/congestion" "github.com/zhangyunhao116/fastrand" ) const ( // InitialMaxDatagramSize is the default maximum packet size used in QUIC for congestion window computations in bytes. - InitialMaxDatagramSize = 1252 - InitialPacketSizeIPv4 = 1252 - InitialPacketSizeIPv6 = 1232 + InitialMaxDatagramSize = 1280 + InitialPacketSize = 1280 InitialCongestionWindow = 32 DefaultBBRMaxCongestionWindow = 10000 ) -func GetInitialPacketSize(addr net.Addr) congestion.ByteCount { - maxSize := congestion.ByteCount(1200) - // If this is not a UDP address, we don't know anything about the MTU. - // Use the minimum size of an Initial packet as the max packet size. - if udpAddr, ok := addr.(*net.UDPAddr); ok { - if udpAddr.IP.To4() != nil { - maxSize = InitialPacketSizeIPv4 - } else { - maxSize = InitialPacketSizeIPv6 - } - } - return congestion.ByteCount(maxSize) +func GetInitialPacketSize(quicConn quic.Connection) congestion.ByteCount { + return congestion.ByteCount(quicConn.Config().InitialPacketSize) } var ( diff --git a/transport/tuic/congestion/cubic.go b/transport/tuic/congestion/cubic.go index dd491a32..a9bed43a 100644 --- a/transport/tuic/congestion/cubic.go +++ b/transport/tuic/congestion/cubic.go @@ -21,7 +21,7 @@ const ( cubeCongestionWindowScale = 410 cubeFactor congestion.ByteCount = 1 << cubeScale / cubeCongestionWindowScale / maxDatagramSize // TODO: when re-enabling cubic, make sure to use the actual packet size here - maxDatagramSize = congestion.ByteCount(InitialPacketSizeIPv4) + maxDatagramSize = congestion.ByteCount(InitialPacketSize) ) const defaultNumConnections = 1 diff --git a/transport/tuic/congestion_v2/bbr_sender.go b/transport/tuic/congestion_v2/bbr_sender.go index 084f85b1..54705978 100644 --- a/transport/tuic/congestion_v2/bbr_sender.go +++ b/transport/tuic/congestion_v2/bbr_sender.go @@ -4,9 +4,9 @@ package congestion import ( "fmt" - "net" "time" + "github.com/metacubex/quic-go" "github.com/metacubex/quic-go/congestion" "github.com/zhangyunhao116/fastrand" @@ -30,7 +30,7 @@ const ( // Constants based on TCP defaults. // The minimum CWND to ensure delayed acks don't reduce bandwidth measurements. // Does not inflate the pacing rate. - defaultMinimumCongestionWindow = 4 * congestion.ByteCount(congestion.InitialPacketSizeIPv4) + defaultMinimumCongestionWindow = 4 * congestion.ByteCount(congestion.InitialPacketSize) // The gain used for the STARTUP, equal to 2/ln(2). defaultHighGain = 2.885 @@ -931,16 +931,6 @@ func bdpFromRttAndBandwidth(rtt time.Duration, bandwidth Bandwidth) congestion.B return congestion.ByteCount(rtt) * congestion.ByteCount(bandwidth) / congestion.ByteCount(BytesPerSecond) / congestion.ByteCount(time.Second) } -func GetInitialPacketSize(addr net.Addr) congestion.ByteCount { - // If this is not a UDP address, we don't know anything about the MTU. - // Use the minimum size of an Initial packet as the max packet size. - if udpAddr, ok := addr.(*net.UDPAddr); ok { - if udpAddr.IP.To4() != nil { - return congestion.InitialPacketSizeIPv4 - } else { - return congestion.InitialPacketSizeIPv6 - } - } else { - return congestion.MinInitialPacketSize - } +func GetInitialPacketSize(quicConn quic.Connection) congestion.ByteCount { + return congestion.ByteCount(quicConn.Config().InitialPacketSize) } diff --git a/transport/tuic/congestion_v2/pacer.go b/transport/tuic/congestion_v2/pacer.go index ecaf3d11..174b3dbe 100644 --- a/transport/tuic/congestion_v2/pacer.go +++ b/transport/tuic/congestion_v2/pacer.go @@ -21,8 +21,8 @@ type Pacer struct { func NewPacer(getBandwidth func() congestion.ByteCount) *Pacer { p := &Pacer{ - budgetAtLastSent: maxBurstPackets * congestion.InitialPacketSizeIPv4, - maxDatagramSize: congestion.InitialPacketSizeIPv4, + budgetAtLastSent: maxBurstPackets * congestion.InitialPacketSize, + maxDatagramSize: congestion.InitialPacketSize, getBandwidth: getBandwidth, } return p From 43bdc76f87165bba04d0435e61b943ced945a872 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 21 May 2024 19:13:44 +0800 Subject: [PATCH 05/38] fix: darwin calculate correct tunIndex https://github.com/MetaCubeX/mihomo/pull/1285 --- listener/sing_tun/server.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 2dc6524e..09bf308c 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -59,15 +59,23 @@ func CalculateInterfaceName(name string) (tunName string) { if err != nil { return } - var tunIndex int + tunIndex := 0 + indexSet := make(map[int]struct{}) for _, netInterface := range interfaces { if strings.HasPrefix(netInterface.Name, tunName) { index, parseErr := strconv.ParseInt(netInterface.Name[len(tunName):], 10, 16) if parseErr == nil { - tunIndex = int(index) + 1 + indexSet[int(index)] = struct{}{} } } } + for index := range indexSet { + if index == tunIndex { + tunIndex += 1 + } else { // indexSet already sorted and distinct, so this tunIndex nobody used + break + } + } tunName = F.ToString(tunName, tunIndex) return } From ac2506154f4affa1986f2621795f024cc53cf5e4 Mon Sep 17 00:00:00 2001 From: 5aaee9 <7685264+5aaee9@users.noreply.github.com> Date: Tue, 21 May 2024 23:21:57 +0800 Subject: [PATCH 06/38] fix: possibly using released buffer in tproxy (#1286) --- listener/tproxy/packet.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/listener/tproxy/packet.go b/listener/tproxy/packet.go index e4852665..b038d954 100644 --- a/listener/tproxy/packet.go +++ b/listener/tproxy/packet.go @@ -105,9 +105,9 @@ func listenLocalConn(rAddr, lAddr net.Addr, tunnel C.Tunnel) (*net.UDPConn, erro buf := pool.Get(pool.UDPBufferSize) br, err := lc.Read(buf) if err != nil { - pool.Put(buf) if errors.Is(err, net.ErrClosed) { log.Debugln("TProxy local conn listener exit.. rAddr=%s lAddr=%s", rAddr.String(), lAddr.String()) + pool.Put(buf) return } } From 5eb8958ff2ed8a45f47a4a79357160ca52bbef0b Mon Sep 17 00:00:00 2001 From: moexiami <1927254+Xiami2012@users.noreply.github.com> Date: Tue, 21 May 2024 23:31:28 +0800 Subject: [PATCH 07/38] fix: correct type for vmess.ws-opts.path in ConvertsV2Ray (#1145) It should be a string for the following reasons: 1. During conversion, it is conditionally assigned with `wsOpts["path"] = path.(string)` 2. After conversion, it is decoded into `WSOptions.Path` in `adapter/outbound/vmess.go` which requires a string. --- common/convert/converter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/convert/converter.go b/common/convert/converter.go index 222dd9fa..19886d25 100644 --- a/common/convert/converter.go +++ b/common/convert/converter.go @@ -333,7 +333,7 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { case "ws", "httpupgrade": headers := make(map[string]any) wsOpts := make(map[string]any) - wsOpts["path"] = []string{"/"} + wsOpts["path"] = "/" if host, ok := values["host"]; ok && host != "" { headers["Host"] = host.(string) } From 71922dd0b1098ca09abd62a33ffbcbce77bb9aba Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 21 May 2024 23:52:56 +0800 Subject: [PATCH 08/38] fix: bad usage for exec in sing-tun https://github.com/MetaCubeX/mihomo/issues/1234 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ad2492ff..08e5b5aa 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 - github.com/metacubex/sing-tun v0.2.7-0.20240512075008-89e7c6208eec + github.com/metacubex/sing-tun v0.2.7-0.20240521155100-e8316a45a414 github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 diff --git a/go.sum b/go.sum index 0b4b8d3c..b9f1af14 100644 --- a/go.sum +++ b/go.sum @@ -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.7-0.20240512075008-89e7c6208eec h1:K4Wq3GOdLZ/xcqwyzAt4kmYQrjokyKQ3u/Xh5Yft14U= -github.com/metacubex/sing-tun v0.2.7-0.20240512075008-89e7c6208eec/go.mod h1:4VsMwZH1IlgPGFK1ZbBomZ/B2MYkTgs2+gnBAr5GOIo= +github.com/metacubex/sing-tun v0.2.7-0.20240521155100-e8316a45a414 h1:IPxTZgQV6fVUBS8tozLMSFPHV3imYc/NbuGfp0bLQq0= +github.com/metacubex/sing-tun v0.2.7-0.20240521155100-e8316a45a414/go.mod h1:4VsMwZH1IlgPGFK1ZbBomZ/B2MYkTgs2+gnBAr5GOIo= 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-20240321042214-224f96122a63 h1:AGyIB55UfQm/0ZH0HtQO9u3l//yjtHUpjeRjjPGfGRI= From 0b6ae6ffb881872e3ff7a0b4454bb43a1b178dd3 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 22 May 2024 09:00:59 +0800 Subject: [PATCH 09/38] feat: add `ss-opts` for trojan outbound like trojan-go's `shadowsocks` config https://github.com/MetaCubeX/mihomo/issues/1269 --- adapter/outbound/trojan.go | 43 ++++++++++++++++++++++++++++++++++++++ docs/config.yaml | 4 ++++ 2 files changed, 47 insertions(+) diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index b14761a4..938a8858 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -3,6 +3,7 @@ package outbound import ( "context" "crypto/tls" + "errors" "fmt" "net" "net/http" @@ -15,6 +16,7 @@ import ( tlsC "github.com/metacubex/mihomo/component/tls" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/transport/gun" + "github.com/metacubex/mihomo/transport/shadowsocks/core" "github.com/metacubex/mihomo/transport/trojan" ) @@ -29,6 +31,8 @@ type Trojan struct { transport *gun.TransportWrap realityConfig *tlsC.RealityConfig + + ssCipher core.Cipher } type TrojanOption struct { @@ -46,9 +50,17 @@ type TrojanOption struct { RealityOpts RealityOptions `proxy:"reality-opts,omitempty"` GrpcOpts GrpcOptions `proxy:"grpc-opts,omitempty"` WSOpts WSOptions `proxy:"ws-opts,omitempty"` + SSOpts TrojanSSOption `proxy:"ss-opts,omitempty"` ClientFingerprint string `proxy:"client-fingerprint,omitempty"` } +// TrojanSSOption from https://github.com/p4gefau1t/trojan-go/blob/v0.10.6/tunnel/shadowsocks/config.go#L5 +type TrojanSSOption struct { + Enabled bool `proxy:"enabled,omitempty"` + Method string `proxy:"method,omitempty"` + Password string `proxy:"password,omitempty"` +} + func (t *Trojan) plainStream(ctx context.Context, c net.Conn) (net.Conn, error) { if t.option.Network == "ws" { host, port, _ := net.SplitHostPort(t.addr) @@ -95,6 +107,10 @@ func (t *Trojan) StreamConnContext(ctx context.Context, c net.Conn, metadata *C. return nil, fmt.Errorf("%s connect error: %w", t.addr, err) } + if t.ssCipher != nil { + c = t.ssCipher.StreamConn(c) + } + if metadata.NetWork == C.UDP { err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata)) return c, err @@ -112,6 +128,10 @@ func (t *Trojan) DialContext(ctx context.Context, metadata *C.Metadata, opts ... return nil, err } + if t.ssCipher != nil { + c = t.ssCipher.StreamConn(c) + } + if err = t.instance.WriteHeader(c, trojan.CommandTCP, serializesSocksAddr(metadata)); err != nil { c.Close() return nil, err @@ -161,6 +181,11 @@ func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata, defer func(c net.Conn) { safeConnClose(c, err) }(c) + + if t.ssCipher != nil { + c = t.ssCipher.StreamConn(c) + } + err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata)) if err != nil { return nil, err @@ -193,6 +218,10 @@ func (t *Trojan) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, me return nil, fmt.Errorf("%s connect error: %w", t.addr, err) } + if t.ssCipher != nil { + c = t.ssCipher.StreamConn(c) + } + err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata)) if err != nil { return nil, err @@ -257,6 +286,20 @@ func NewTrojan(option TrojanOption) (*Trojan, error) { } tOption.Reality = t.realityConfig + if option.SSOpts.Enabled { + if option.SSOpts.Password == "" { + return nil, errors.New("empty password") + } + if option.SSOpts.Method == "" { + option.SSOpts.Method = "AES-128-GCM" + } + ciph, err := core.PickCipher(option.SSOpts.Method, nil, option.SSOpts.Password) + if err != nil { + return nil, err + } + t.ssCipher = ciph + } + if option.Network == "grpc" { dialFn := func(network, addr string) (net.Conn, error) { var err error diff --git a/docs/config.yaml b/docs/config.yaml index 987491e3..fe850163 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -611,6 +611,10 @@ proxies: # socks5 # - h2 # - http/1.1 # skip-cert-verify: true + # ss-opts: # like trojan-go's `shadowsocks` config + # enabled: false + # method: aes-128-gcm # aes-128-gcm/aes-256-gcm/chacha20-ietf-poly1305 + # password: "example" - name: trojan-grpc server: server From 846bdfa8129b9b984a62f04204b4a6925a016834 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sat, 25 May 2024 08:09:59 +0800 Subject: [PATCH 10/38] chore: Allow customization of GLOBAL --- config/config.go | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/config/config.go b/config/config.go index 9bc0afc8..74a2053e 100644 --- a/config/config.go +++ b/config/config.go @@ -716,8 +716,11 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[ groupsConfig := cfg.ProxyGroup providersConfig := cfg.ProxyProvider - var proxyList []string - var AllProxies []string + var ( + proxyList []string + AllProxies []string + hasGlobal bool + ) proxiesList := list.New() groupsList := list.New() @@ -750,6 +753,9 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[ if !existName { return nil, nil, fmt.Errorf("proxy group %d: missing name", idx) } + if groupName == "GLOBAL" { + hasGlobal = true + } proxyList = append(proxyList, groupName) groupsList.PushBack(mapping) } @@ -801,13 +807,15 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[ pd, _ := provider.NewCompatibleProvider(provider.ReservedName, ps, hc) providersMap[provider.ReservedName] = pd - global := outboundgroup.NewSelector( - &outboundgroup.GroupCommonOption{ - Name: "GLOBAL", - }, - []providerTypes.ProxyProvider{pd}, - ) - proxies["GLOBAL"] = adapter.NewProxy(global) + if !hasGlobal { + global := outboundgroup.NewSelector( + &outboundgroup.GroupCommonOption{ + Name: "GLOBAL", + }, + []providerTypes.ProxyProvider{pd}, + ) + proxies["GLOBAL"] = adapter.NewProxy(global) + } ProxiesList = proxiesList GroupsList = groupsList if ParsingProxiesCallback != nil { From 7eb70aeb4d1c68474f5abbfc96c0823ff89175b2 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 29 May 2024 00:08:17 +0800 Subject: [PATCH 11/38] fix: windows build number --- constant/features/version_windows.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/constant/features/version_windows.go b/constant/features/version_windows.go index 192df950..2f756188 100644 --- a/constant/features/version_windows.go +++ b/constant/features/version_windows.go @@ -6,5 +6,5 @@ func init() { version := windows.RtlGetVersion() WindowsMajorVersion = version.MajorVersion WindowsMinorVersion = version.MinorVersion - WindowsBuildNumber = version.MinorVersion + WindowsBuildNumber = version.BuildNumber } From d3fea909e98c0645aa73cfc60f52cb9038e3ebfa Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 30 May 2024 10:39:17 +0800 Subject: [PATCH 12/38] chore: remove tfo windows support Golang officially decided not to open `internal/poll.execIO` to third-party libraries after 1.23 was released, so we can only choose to remove tfo support on the Windows platform. --- adapter/inbound/listen.go | 14 +------------- adapter/inbound/listen_unix.go | 23 +++++++++++++++++++++++ adapter/inbound/listen_windows.go | 15 +++++++++++++++ component/dialer/tfo.go | 17 ----------------- component/dialer/tfo_unix.go | 25 +++++++++++++++++++++++++ component/dialer/tfo_windows.go | 15 ++++++++------- 6 files changed, 72 insertions(+), 37 deletions(-) create mode 100644 adapter/inbound/listen_unix.go create mode 100644 adapter/inbound/listen_windows.go create mode 100644 component/dialer/tfo_unix.go diff --git a/adapter/inbound/listen.go b/adapter/inbound/listen.go index 18dc1bc2..edbccea7 100644 --- a/adapter/inbound/listen.go +++ b/adapter/inbound/listen.go @@ -3,22 +3,10 @@ package inbound import ( "context" "net" - - "github.com/metacubex/tfo-go" ) -var ( - lc = tfo.ListenConfig{ - DisableTFO: true, - } -) - -func SetTfo(open bool) { - lc.DisableTFO = !open -} - func SetMPTCP(open bool) { - setMultiPathTCP(&lc.ListenConfig, open) + setMultiPathTCP(getListenConfig(), open) } func ListenContext(ctx context.Context, network, address string) (net.Listener, error) { diff --git a/adapter/inbound/listen_unix.go b/adapter/inbound/listen_unix.go new file mode 100644 index 00000000..bb78adb2 --- /dev/null +++ b/adapter/inbound/listen_unix.go @@ -0,0 +1,23 @@ +//go:build unix + +package inbound + +import ( + "net" + + "github.com/metacubex/tfo-go" +) + +var ( + lc = tfo.ListenConfig{ + DisableTFO: true, + } +) + +func SetTfo(open bool) { + lc.DisableTFO = !open +} + +func getListenConfig() *net.ListenConfig { + return &lc.ListenConfig +} diff --git a/adapter/inbound/listen_windows.go b/adapter/inbound/listen_windows.go new file mode 100644 index 00000000..a4223e2b --- /dev/null +++ b/adapter/inbound/listen_windows.go @@ -0,0 +1,15 @@ +package inbound + +import ( + "net" +) + +var ( + lc = net.ListenConfig{} +) + +func SetTfo(open bool) {} + +func getListenConfig() *net.ListenConfig { + return &lc +} diff --git a/component/dialer/tfo.go b/component/dialer/tfo.go index 76fe94d0..bc32b38a 100644 --- a/component/dialer/tfo.go +++ b/component/dialer/tfo.go @@ -5,12 +5,8 @@ import ( "io" "net" "time" - - "github.com/metacubex/tfo-go" ) -var DisableTFO = false - type tfoConn struct { net.Conn closed bool @@ -124,16 +120,3 @@ func (c *tfoConn) ReaderReplaceable() bool { func (c *tfoConn) WriterReplaceable() bool { return c.Conn != nil } - -func dialTFO(ctx context.Context, netDialer net.Dialer, network, address string) (net.Conn, error) { - ctx, cancel := context.WithTimeout(context.Background(), DefaultTCPTimeout) - dialer := tfo.Dialer{Dialer: netDialer, DisableTFO: false} - return &tfoConn{ - dialed: make(chan bool, 1), - cancel: cancel, - ctx: ctx, - dialFn: func(ctx context.Context, earlyData []byte) (net.Conn, error) { - return dialer.DialContext(ctx, network, address, earlyData) - }, - }, nil -} diff --git a/component/dialer/tfo_unix.go b/component/dialer/tfo_unix.go new file mode 100644 index 00000000..b8908849 --- /dev/null +++ b/component/dialer/tfo_unix.go @@ -0,0 +1,25 @@ +//go:build unix + +package dialer + +import ( + "context" + "net" + + "github.com/metacubex/tfo-go" +) + +const DisableTFO = false + +func dialTFO(ctx context.Context, netDialer net.Dialer, network, address string) (net.Conn, error) { + ctx, cancel := context.WithTimeout(context.Background(), DefaultTCPTimeout) + dialer := tfo.Dialer{Dialer: netDialer, DisableTFO: false} + return &tfoConn{ + dialed: make(chan bool, 1), + cancel: cancel, + ctx: ctx, + dialFn: func(ctx context.Context, earlyData []byte) (net.Conn, error) { + return dialer.DialContext(ctx, network, address, earlyData) + }, + }, nil +} diff --git a/component/dialer/tfo_windows.go b/component/dialer/tfo_windows.go index 63266118..f1dddcf4 100644 --- a/component/dialer/tfo_windows.go +++ b/component/dialer/tfo_windows.go @@ -1,11 +1,12 @@ package dialer -import "github.com/metacubex/mihomo/constant/features" +import ( + "context" + "net" +) -func init() { - // According to MSDN, this option is available since Windows 10, 1607 - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms738596(v=vs.85).aspx - if features.WindowsMajorVersion < 10 || (features.WindowsMajorVersion == 10 && features.WindowsBuildNumber < 14393) { - DisableTFO = true - } +const DisableTFO = true + +func dialTFO(ctx context.Context, netDialer net.Dialer, network, address string) (net.Conn, error) { + return netDialer.DialContext(ctx, network, address) } From 39eda257a77507bee161a422df053f5cea236590 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 31 May 2024 11:31:17 +0800 Subject: [PATCH 13/38] chore: replace zhangyunhao116/fastrand to our metacubex/randv2 --- adapter/outbound/hysteria2.go | 4 +-- adapter/outbound/ssh.go | 8 ++--- common/convert/util.go | 6 ++-- common/pool/alloc_test.go | 4 +-- common/utils/uuid.go | 38 +++++++++++++++++----- component/resolver/host.go | 4 +-- component/resolver/resolver.go | 10 +++--- component/tls/reality.go | 6 ++-- dns/client.go | 4 +-- go.mod | 2 +- go.sum | 4 +-- transport/hysteria/conns/udp/hop.go | 6 ++-- transport/hysteria/conns/wechat/obfs.go | 4 +-- transport/hysteria/core/client.go | 4 +-- transport/hysteria/obfs/xplus.go | 5 ++- transport/simple-obfs/http.go | 7 ++-- transport/simple-obfs/tls.go | 7 ++-- transport/ssr/obfs/http_simple.go | 10 +++--- transport/ssr/obfs/random_head.go | 7 ++-- transport/ssr/obfs/tls1.2_ticket_auth.go | 11 ++++--- transport/ssr/protocol/auth_aes128_sha1.go | 17 +++++----- transport/ssr/protocol/auth_sha1_v4.go | 6 ++-- transport/ssr/protocol/base.go | 7 ++-- transport/ssr/protocol/protocol.go | 4 +-- transport/tuic/congestion/bbr_sender.go | 4 +-- transport/tuic/congestion_v2/bbr_sender.go | 4 +-- transport/tuic/v4/client.go | 4 +-- transport/tuic/v5/client.go | 4 +-- transport/tuic/v5/packet.go | 4 +-- transport/vless/vision/padding.go | 10 +++--- transport/vmess/conn.go | 9 ++--- transport/vmess/h2.go | 4 +-- transport/vmess/http.go | 8 ++--- transport/vmess/vmess.go | 4 +-- transport/vmess/websocket.go | 7 ++-- 35 files changed, 136 insertions(+), 111 deletions(-) diff --git a/adapter/outbound/hysteria2.go b/adapter/outbound/hysteria2.go index 0ad7c214..b8abf39c 100644 --- a/adapter/outbound/hysteria2.go +++ b/adapter/outbound/hysteria2.go @@ -21,8 +21,8 @@ import ( "github.com/metacubex/sing-quic/hysteria2" + "github.com/metacubex/randv2" M "github.com/sagernet/sing/common/metadata" - "github.com/zhangyunhao116/fastrand" ) func init() { @@ -165,7 +165,7 @@ func NewHysteria2(option Hysteria2Option) (*Hysteria2, error) { }) 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)) + return resolveUDPAddrWithPrefer(ctx, "udp", serverAddress[randv2.IntN(len(serverAddress))], C.NewDNSPrefer(option.IPVersion)) } if option.HopInterval == 0 { diff --git a/adapter/outbound/ssh.go b/adapter/outbound/ssh.go index a0efabca..8b2776a6 100644 --- a/adapter/outbound/ssh.go +++ b/adapter/outbound/ssh.go @@ -17,7 +17,7 @@ import ( "github.com/metacubex/mihomo/component/proxydialer" C "github.com/metacubex/mihomo/constant" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" "golang.org/x/crypto/ssh" ) @@ -180,10 +180,10 @@ func NewSsh(option SshOption) (*Ssh, error) { } version := "SSH-2.0-OpenSSH_" - if fastrand.Intn(2) == 0 { - version += "7." + strconv.Itoa(fastrand.Intn(10)) + if randv2.IntN(2) == 0 { + version += "7." + strconv.Itoa(randv2.IntN(10)) } else { - version += "8." + strconv.Itoa(fastrand.Intn(9)) + version += "8." + strconv.Itoa(randv2.IntN(9)) } config.ClientVersion = version diff --git a/common/convert/util.go b/common/convert/util.go index a715b556..ab006374 100644 --- a/common/convert/util.go +++ b/common/convert/util.go @@ -8,8 +8,8 @@ import ( "github.com/metacubex/mihomo/common/utils" + "github.com/metacubex/randv2" "github.com/metacubex/sing-shadowsocks/shadowimpl" - "github.com/zhangyunhao116/fastrand" ) var hostsSuffix = []string{ @@ -302,11 +302,11 @@ func RandHost() string { prefix += string(buf[6:8]) + "-" prefix += string(buf[len(buf)-8:]) - return prefix + hostsSuffix[fastrand.Intn(hostsLen)] + return prefix + hostsSuffix[randv2.IntN(hostsLen)] } func RandUserAgent() string { - return userAgents[fastrand.Intn(uaLen)] + return userAgents[randv2.IntN(uaLen)] } func SetUserAgent(header http.Header) { diff --git a/common/pool/alloc_test.go b/common/pool/alloc_test.go index 0ac72ee0..c76ff26a 100644 --- a/common/pool/alloc_test.go +++ b/common/pool/alloc_test.go @@ -3,8 +3,8 @@ package pool import ( "testing" + "github.com/metacubex/randv2" "github.com/stretchr/testify/assert" - "github.com/zhangyunhao116/fastrand" ) func TestAllocGet(t *testing.T) { @@ -43,6 +43,6 @@ func TestAllocPutThenGet(t *testing.T) { func BenchmarkMSB(b *testing.B) { for i := 0; i < b.N; i++ { - msb(fastrand.Int()) + msb(randv2.Int()) } } diff --git a/common/utils/uuid.go b/common/utils/uuid.go index f559b471..d9a8b789 100644 --- a/common/utils/uuid.go +++ b/common/utils/uuid.go @@ -2,19 +2,39 @@ package utils import ( "github.com/gofrs/uuid/v5" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) -type fastRandReader struct{} +type unsafeRandReader struct{} -func (r fastRandReader) Read(p []byte) (int, error) { - return fastrand.Read(p) +func (r unsafeRandReader) Read(p []byte) (n int, err error) { + // modify from https://github.com/golang/go/blob/587c3847da81aa7cfc3b3db2677c8586c94df13a/src/runtime/rand.go#L70-L89 + // Inspired by wyrand. + n = len(p) + v := randv2.Uint64() + for len(p) > 0 { + v ^= 0xa0761d6478bd642f + v *= 0xe7037ed1a0b428db + size := 8 + if len(p) < 8 { + size = len(p) + } + for i := 0; i < size; i++ { + p[i] ^= byte(v >> (8 * i)) + } + p = p[size:] + v = v>>32 | v<<32 + } + + return } -var UnsafeUUIDGenerator = uuid.NewGenWithOptions(uuid.WithRandomReader(fastRandReader{})) +var UnsafeRandReader = unsafeRandReader{} + +var UnsafeUUIDGenerator = uuid.NewGenWithOptions(uuid.WithRandomReader(UnsafeRandReader)) func NewUUIDV1() uuid.UUID { - u, _ := UnsafeUUIDGenerator.NewV1() // fastrand.Read wouldn't cause error, so ignore err is safe + u, _ := UnsafeUUIDGenerator.NewV1() // unsafeRandReader wouldn't cause error, so ignore err is safe return u } @@ -23,7 +43,7 @@ func NewUUIDV3(ns uuid.UUID, name string) uuid.UUID { } func NewUUIDV4() uuid.UUID { - u, _ := UnsafeUUIDGenerator.NewV4() // fastrand.Read wouldn't cause error, so ignore err is safe + u, _ := UnsafeUUIDGenerator.NewV4() // unsafeRandReader wouldn't cause error, so ignore err is safe return u } @@ -32,12 +52,12 @@ func NewUUIDV5(ns uuid.UUID, name string) uuid.UUID { } func NewUUIDV6() uuid.UUID { - u, _ := UnsafeUUIDGenerator.NewV6() // fastrand.Read wouldn't cause error, so ignore err is safe + u, _ := UnsafeUUIDGenerator.NewV6() // unsafeRandReader wouldn't cause error, so ignore err is safe return u } func NewUUIDV7() uuid.UUID { - u, _ := UnsafeUUIDGenerator.NewV7() // fastrand.Read wouldn't cause error, so ignore err is safe + u, _ := UnsafeUUIDGenerator.NewV7() // unsafeRandReader wouldn't cause error, so ignore err is safe return u } diff --git a/component/resolver/host.go b/component/resolver/host.go index 53fa5924..34da8e9f 100644 --- a/component/resolver/host.go +++ b/component/resolver/host.go @@ -11,7 +11,7 @@ import ( "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/resolver/hosts" "github.com/metacubex/mihomo/component/trie" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) var ( @@ -125,5 +125,5 @@ func (hv HostValue) RandIP() (netip.Addr, error) { if hv.IsDomain { return netip.Addr{}, errors.New("value type is error") } - return hv.IPs[fastrand.Intn(len(hv.IPs))], nil + return hv.IPs[randv2.IntN(len(hv.IPs))], nil } diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index f9b56e47..07c68824 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -12,8 +12,8 @@ import ( "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/trie" + "github.com/metacubex/randv2" "github.com/miekg/dns" - "github.com/zhangyunhao116/fastrand" ) var ( @@ -93,7 +93,7 @@ func ResolveIPv4WithResolver(ctx context.Context, host string, r Resolver) (neti } else if len(ips) == 0 { return netip.Addr{}, fmt.Errorf("%w: %s", ErrIPNotFound, host) } - return ips[fastrand.Intn(len(ips))], nil + return ips[randv2.IntN(len(ips))], nil } // ResolveIPv4 with a host, return ipv4 @@ -149,7 +149,7 @@ func ResolveIPv6WithResolver(ctx context.Context, host string, r Resolver) (neti } else if len(ips) == 0 { return netip.Addr{}, fmt.Errorf("%w: %s", ErrIPNotFound, host) } - return ips[fastrand.Intn(len(ips))], nil + return ips[randv2.IntN(len(ips))], nil } func ResolveIPv6(ctx context.Context, host string) (netip.Addr, error) { @@ -200,9 +200,9 @@ func ResolveIPWithResolver(ctx context.Context, host string, r Resolver) (netip. } ipv4s, ipv6s := SortationAddr(ips) if len(ipv4s) > 0 { - return ipv4s[fastrand.Intn(len(ipv4s))], nil + return ipv4s[randv2.IntN(len(ipv4s))], nil } - return ipv6s[fastrand.Intn(len(ipv6s))], nil + return ipv6s[randv2.IntN(len(ipv6s))], nil } // ResolveIP with a host, return ip and priority return TypeA diff --git a/component/tls/reality.go b/component/tls/reality.go index 48da3e92..ff028257 100644 --- a/component/tls/reality.go +++ b/component/tls/reality.go @@ -22,8 +22,8 @@ import ( "github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/ntp" + "github.com/metacubex/randv2" utls "github.com/metacubex/utls" - "github.com/zhangyunhao116/fastrand" "golang.org/x/crypto/chacha20poly1305" "golang.org/x/crypto/hkdf" "golang.org/x/net/http2" @@ -138,13 +138,13 @@ func realityClientFallback(uConn net.Conn, serverName string, fingerprint utls.C return } request.Header.Set("User-Agent", fingerprint.Client) - request.AddCookie(&http.Cookie{Name: "padding", Value: strings.Repeat("0", fastrand.Intn(32)+30)}) + request.AddCookie(&http.Cookie{Name: "padding", Value: strings.Repeat("0", randv2.IntN(32)+30)}) response, err := client.Do(request) if err != nil { return } //_, _ = io.Copy(io.Discard, response.Body) - time.Sleep(time.Duration(5+fastrand.Int63n(10)) * time.Second) + time.Sleep(time.Duration(5+randv2.IntN(10)) * time.Second) response.Body.Close() client.CloseIdleConnections() } diff --git a/dns/client.go b/dns/client.go index 3b4efed1..a6f0a7d4 100644 --- a/dns/client.go +++ b/dns/client.go @@ -14,8 +14,8 @@ import ( C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" + "github.com/metacubex/randv2" D "github.com/miekg/dns" - "github.com/zhangyunhao116/fastrand" ) type client struct { @@ -65,7 +65,7 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) } else if len(ips) == 0 { return nil, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, c.host) } - ip = ips[fastrand.Intn(len(ips))] + ip = ips[randv2.IntN(len(ips))] } network := "udp" diff --git a/go.mod b/go.mod index 08e5b5aa..bc32a633 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +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.44.1-0.20240521004242-fcd70d587e22 + github.com/metacubex/randv2 v0.2.0 github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 @@ -44,7 +45,6 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.9.0 github.com/wk8/go-ordered-map/v2 v2.1.8 - github.com/zhangyunhao116/fastrand v0.4.0 go.uber.org/automaxprocs v1.5.3 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba golang.org/x/crypto v0.23.0 diff --git a/go.sum b/go.sum index b9f1af14..6dbbfd77 100644 --- a/go.sum +++ b/go.sum @@ -106,6 +106,8 @@ github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec h1:HxreOiFTUrJXJa github.com/metacubex/gvisor v0.0.0-20240320004321-933faba989ec/go.mod h1:8BVmQ+3cxjqzWElafm24rb2Ae4jRI6vAXNXWqWjfrXw= github.com/metacubex/quic-go v0.44.1-0.20240521004242-fcd70d587e22 h1:hsQ0b2A509b6ubnLtLOcUgZ8vOb+d/667zVEJ1T2fao= github.com/metacubex/quic-go v0.44.1-0.20240521004242-fcd70d587e22/go.mod h1:88wAATpevav4xdy5N8oejQ2cbbI6EcLYEklFeo+qywA= +github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= +github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1 h1:7hDHLTmjgtRoAp59STwPQpe5Pinwi4cWex+FB3Ohvco= github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 h1:Wr4g1HCb5Z/QIFwFiVNjO2qL+dRu25+Mdn9xtAZZ+ew= @@ -210,8 +212,6 @@ github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/ github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= 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.4.0 h1:86QB6Y+GGgLZRFRDCjMmAS28QULwspK9sgL5d1Bx3H4= -github.com/zhangyunhao116/fastrand v0.4.0/go.mod h1:vIyo6EyBhjGKpZv6qVlkPl4JVAklpMM4DSKzbAkMguA= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ= go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= diff --git a/transport/hysteria/conns/udp/hop.go b/transport/hysteria/conns/udp/hop.go index eb0732f0..0a888749 100644 --- a/transport/hysteria/conns/udp/hop.go +++ b/transport/hysteria/conns/udp/hop.go @@ -12,7 +12,7 @@ import ( "github.com/metacubex/mihomo/transport/hysteria/obfs" "github.com/metacubex/mihomo/transport/hysteria/utils" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) const ( @@ -86,7 +86,7 @@ func NewObfsUDPHopClientPacketConn(server string, serverPorts string, hopInterva serverAddrs: serverAddrs, hopInterval: hopInterval, obfs: obfs, - addrIndex: fastrand.Intn(len(serverAddrs)), + addrIndex: randv2.IntN(len(serverAddrs)), recvQueue: make(chan *udpPacket, packetQueueSize), closeChan: make(chan struct{}), bufPool: sync.Pool{ @@ -177,7 +177,7 @@ func (c *ObfsUDPHopClientPacketConn) hop(dialer utils.PacketDialer, rAddr net.Ad _ = trySetPacketConnWriteBuffer(c.currentConn, c.writeBufferSize) } go c.recvRoutine(c.currentConn) - c.addrIndex = fastrand.Intn(len(c.serverAddrs)) + c.addrIndex = randv2.IntN(len(c.serverAddrs)) } func (c *ObfsUDPHopClientPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { diff --git a/transport/hysteria/conns/wechat/obfs.go b/transport/hysteria/conns/wechat/obfs.go index 4266d268..c5ec47ee 100644 --- a/transport/hysteria/conns/wechat/obfs.go +++ b/transport/hysteria/conns/wechat/obfs.go @@ -9,7 +9,7 @@ import ( "github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/transport/hysteria/obfs" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) const udpBufferSize = 65535 @@ -31,7 +31,7 @@ func NewObfsWeChatUDPConn(orig net.PacketConn, obfs obfs.Obfuscator) *ObfsWeChat obfs: obfs, readBuf: make([]byte, udpBufferSize), writeBuf: make([]byte, udpBufferSize), - sn: fastrand.Uint32() & 0xFFFF, + sn: randv2.Uint32() & 0xFFFF, } } diff --git a/transport/hysteria/core/client.go b/transport/hysteria/core/client.go index 97c9225e..60db8fdf 100644 --- a/transport/hysteria/core/client.go +++ b/transport/hysteria/core/client.go @@ -19,7 +19,7 @@ import ( "github.com/lunixbochs/struc" "github.com/metacubex/quic-go" "github.com/metacubex/quic-go/congestion" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) var ( @@ -405,7 +405,7 @@ func (c *quicPktConn) WriteTo(p []byte, addr string) error { var errSize *quic.DatagramTooLargeError if errors.As(err, &errSize) { // need to frag - msg.MsgID = uint16(fastrand.Intn(0xFFFF)) + 1 // msgID must be > 0 when fragCount > 1 + msg.MsgID = uint16(randv2.IntN(0xFFFF)) + 1 // msgID must be > 0 when fragCount > 1 fragMsgs := fragUDPMessage(msg, int(errSize.MaxDatagramPayloadSize)) for _, fragMsg := range fragMsgs { msgBuf.Reset() diff --git a/transport/hysteria/obfs/xplus.go b/transport/hysteria/obfs/xplus.go index 171bf281..4d1e3f29 100644 --- a/transport/hysteria/obfs/xplus.go +++ b/transport/hysteria/obfs/xplus.go @@ -1,9 +1,8 @@ package obfs import ( + "crypto/rand" "crypto/sha256" - - "github.com/zhangyunhao116/fastrand" ) // [salt][obfuscated payload] @@ -35,7 +34,7 @@ func (x *XPlusObfuscator) Deobfuscate(in []byte, out []byte) int { } func (x *XPlusObfuscator) Obfuscate(in []byte, out []byte) int { - _, _ = fastrand.Read(out[:saltLen]) // salt + _, _ = rand.Read(out[:saltLen]) // salt // Obfuscate the payload key := sha256.Sum256(append(x.Key, out[:saltLen]...)) for i, c := range in { diff --git a/transport/simple-obfs/http.go b/transport/simple-obfs/http.go index a38b1d69..9c3f8e00 100644 --- a/transport/simple-obfs/http.go +++ b/transport/simple-obfs/http.go @@ -2,6 +2,7 @@ package obfs import ( "bytes" + "crypto/rand" "encoding/base64" "fmt" "io" @@ -10,7 +11,7 @@ import ( "github.com/metacubex/mihomo/common/pool" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) // HTTPObfs is shadowsocks http simple-obfs implementation @@ -64,12 +65,12 @@ func (ho *HTTPObfs) Read(b []byte) (int, error) { func (ho *HTTPObfs) Write(b []byte) (int, error) { if ho.firstRequest { randBytes := make([]byte, 16) - fastrand.Read(randBytes) + rand.Read(randBytes) req, err := http.NewRequest("GET", fmt.Sprintf("http://%s/", ho.host), bytes.NewBuffer(b[:])) if err != nil { return 0, err } - req.Header.Set("User-Agent", fmt.Sprintf("curl/7.%d.%d", fastrand.Int()%54, fastrand.Int()%2)) + req.Header.Set("User-Agent", fmt.Sprintf("curl/7.%d.%d", randv2.Int()%54, randv2.Int()%2)) req.Header.Set("Upgrade", "websocket") req.Header.Set("Connection", "Upgrade") req.Host = ho.host diff --git a/transport/simple-obfs/tls.go b/transport/simple-obfs/tls.go index 78317f0a..a0cbc350 100644 --- a/transport/simple-obfs/tls.go +++ b/transport/simple-obfs/tls.go @@ -2,14 +2,13 @@ package obfs import ( "bytes" + "crypto/rand" "encoding/binary" "io" "net" "time" "github.com/metacubex/mihomo/common/pool" - - "github.com/zhangyunhao116/fastrand" ) const ( @@ -127,8 +126,8 @@ func NewTLSObfs(conn net.Conn, server string) net.Conn { func makeClientHelloMsg(data []byte, server string) []byte { random := make([]byte, 28) sessionID := make([]byte, 32) - fastrand.Read(random) - fastrand.Read(sessionID) + rand.Read(random) + rand.Read(sessionID) buf := &bytes.Buffer{} diff --git a/transport/ssr/obfs/http_simple.go b/transport/ssr/obfs/http_simple.go index 359ca342..d59b490d 100644 --- a/transport/ssr/obfs/http_simple.go +++ b/transport/ssr/obfs/http_simple.go @@ -10,7 +10,7 @@ import ( "github.com/metacubex/mihomo/common/pool" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) func init() { @@ -82,7 +82,7 @@ func (c *httpConn) Write(b []byte) (int, error) { bLength := len(b) headDataLength := bLength if bLength-headLength > 64 { - headDataLength = headLength + fastrand.Intn(65) + headDataLength = headLength + randv2.IntN(65) } headData := b[:headDataLength] b = b[headDataLength:] @@ -100,7 +100,7 @@ func (c *httpConn) Write(b []byte) (int, error) { } } hosts := strings.Split(host, ",") - host = hosts[fastrand.Intn(len(hosts))] + host = hosts[randv2.IntN(len(hosts))] buf := pool.GetBuffer() defer pool.PutBuffer(buf) @@ -119,7 +119,7 @@ func (c *httpConn) Write(b []byte) (int, error) { buf.WriteString(body + "\r\n\r\n") } else { buf.WriteString("User-Agent: ") - buf.WriteString(userAgent[fastrand.Intn(len(userAgent))]) + buf.WriteString(userAgent[randv2.IntN(len(userAgent))]) buf.WriteString("\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.8\r\nAccept-Encoding: gzip, deflate\r\n") if c.post { packBoundary(buf) @@ -147,7 +147,7 @@ func packBoundary(buf *bytes.Buffer) { buf.WriteString("Content-Type: multipart/form-data; boundary=") set := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" for i := 0; i < 32; i++ { - buf.WriteByte(set[fastrand.Intn(62)]) + buf.WriteByte(set[randv2.IntN(62)]) } buf.WriteString("\r\n") } diff --git a/transport/ssr/obfs/random_head.go b/transport/ssr/obfs/random_head.go index 9a2072fe..a5ad2dab 100644 --- a/transport/ssr/obfs/random_head.go +++ b/transport/ssr/obfs/random_head.go @@ -1,13 +1,14 @@ package obfs import ( + "crypto/rand" "encoding/binary" "hash/crc32" "net" "github.com/metacubex/mihomo/common/pool" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) func init() { @@ -54,10 +55,10 @@ func (c *randomHeadConn) Write(b []byte) (int, error) { c.buf = append(c.buf, b...) if !c.hasSentHeader { c.hasSentHeader = true - dataLength := fastrand.Intn(96) + 4 + dataLength := randv2.IntN(96) + 4 buf := pool.Get(dataLength + 4) defer pool.Put(buf) - fastrand.Read(buf[:dataLength]) + rand.Read(buf[:dataLength]) binary.LittleEndian.PutUint32(buf[dataLength:], 0xffffffff-crc32.ChecksumIEEE(buf[:dataLength])) _, err := c.Conn.Write(buf) return len(b), err diff --git a/transport/ssr/obfs/tls1.2_ticket_auth.go b/transport/ssr/obfs/tls1.2_ticket_auth.go index d5e3ca88..d5955edc 100644 --- a/transport/ssr/obfs/tls1.2_ticket_auth.go +++ b/transport/ssr/obfs/tls1.2_ticket_auth.go @@ -3,6 +3,7 @@ package obfs import ( "bytes" "crypto/hmac" + "crypto/rand" "encoding/binary" "net" "strings" @@ -11,7 +12,7 @@ import ( "github.com/metacubex/mihomo/common/pool" "github.com/metacubex/mihomo/transport/ssr/tools" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) func init() { @@ -26,7 +27,7 @@ type tls12Ticket struct { func newTLS12Ticket(b *Base) Obfs { r := &tls12Ticket{Base: b, authData: &authData{}} - fastrand.Read(r.clientID[:]) + rand.Read(r.clientID[:]) return r } @@ -91,7 +92,7 @@ func (c *tls12TicketConn) Write(b []byte) (int, error) { buf := pool.GetBuffer() defer pool.PutBuffer(buf) for len(b) > 2048 { - size := fastrand.Intn(4096) + 100 + size := randv2.IntN(4096) + 100 if len(b) < size { size = len(b) } @@ -197,7 +198,7 @@ func packSNIData(buf *bytes.Buffer, u string) { } func (c *tls12TicketConn) packTicketBuf(buf *bytes.Buffer, u string) { - length := 16 * (fastrand.Intn(17) + 8) + length := 16 * (randv2.IntN(17) + 8) buf.Write([]byte{0, 0x23}) binary.Write(buf, binary.BigEndian, uint16(length)) tools.AppendRandBytes(buf, length) @@ -222,6 +223,6 @@ func (t *tls12Ticket) getHost() string { host = "" } hosts := strings.Split(host, ",") - host = hosts[fastrand.Intn(len(hosts))] + host = hosts[randv2.IntN(len(hosts))] return host } diff --git a/transport/ssr/protocol/auth_aes128_sha1.go b/transport/ssr/protocol/auth_aes128_sha1.go index 6ee4160e..cfd55510 100644 --- a/transport/ssr/protocol/auth_aes128_sha1.go +++ b/transport/ssr/protocol/auth_aes128_sha1.go @@ -2,6 +2,7 @@ package protocol import ( "bytes" + "crypto/rand" "encoding/binary" "math" "net" @@ -13,7 +14,7 @@ import ( "github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/transport/ssr/tools" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) type ( @@ -66,7 +67,7 @@ func (a *authAES128) initUserData() { } if len(a.userKey) == 0 { a.userKey = a.Key - fastrand.Read(a.userID[:]) + rand.Read(a.userID[:]) } } @@ -200,7 +201,7 @@ func (a *authAES128) packData(poolBuf *bytes.Buffer, data []byte, fullDataLength } func trapezoidRandom(max int, d float64) int { - base := fastrand.Float64() + base := randv2.Float64() if d-0 > 1e-6 { a := 1 - d base = (math.Sqrt(a*a+4*d*base) - a) / (2 * d) @@ -221,10 +222,10 @@ func (a *authAES128) getRandDataLengthForPackData(dataLength, fullDataLength int if revLength > -1460 { return trapezoidRandom(revLength+1460, -0.3) } - return fastrand.Intn(32) + return randv2.IntN(32) } if dataLength > 900 { - return fastrand.Intn(revLength) + return randv2.IntN(revLength) } return trapezoidRandom(revLength, -0.3) } @@ -249,7 +250,7 @@ func (a *authAES128) packAuthData(poolBuf *bytes.Buffer, data []byte) { copy(macKey, a.iv) copy(macKey[len(a.iv):], a.Key) - poolBuf.WriteByte(byte(fastrand.Intn(256))) + poolBuf.WriteByte(byte(randv2.IntN(256))) poolBuf.Write(a.hmac(macKey, poolBuf.Bytes())[:6]) poolBuf.Write(a.userID[:]) err := a.authData.putEncryptedData(poolBuf, a.userKey, [2]int{packedAuthDataLength, randDataLength}, a.salt) @@ -265,9 +266,9 @@ func (a *authAES128) packAuthData(poolBuf *bytes.Buffer, data []byte) { func (a *authAES128) getRandDataLengthForPackAuthData(size int) int { if size > 400 { - return fastrand.Intn(512) + return randv2.IntN(512) } - return fastrand.Intn(1024) + return randv2.IntN(1024) } func (a *authAES128) packRandData(poolBuf *bytes.Buffer, size int) { diff --git a/transport/ssr/protocol/auth_sha1_v4.go b/transport/ssr/protocol/auth_sha1_v4.go index ed1a39f1..1c616c3f 100644 --- a/transport/ssr/protocol/auth_sha1_v4.go +++ b/transport/ssr/protocol/auth_sha1_v4.go @@ -11,7 +11,7 @@ import ( "github.com/metacubex/mihomo/common/pool" "github.com/metacubex/mihomo/transport/ssr/tools" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) func init() { @@ -178,7 +178,7 @@ func (a *authSHA1V4) getRandDataLength(size int) int { return 0 } if size > 400 { - return fastrand.Intn(256) + return randv2.IntN(256) } - return fastrand.Intn(512) + return randv2.IntN(512) } diff --git a/transport/ssr/protocol/base.go b/transport/ssr/protocol/base.go index e26a6587..79870177 100644 --- a/transport/ssr/protocol/base.go +++ b/transport/ssr/protocol/base.go @@ -4,6 +4,7 @@ import ( "bytes" "crypto/aes" "crypto/cipher" + "crypto/rand" "encoding/base64" "encoding/binary" "sync" @@ -13,7 +14,7 @@ import ( "github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/transport/shadowsocks/core" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) type Base struct { @@ -38,8 +39,8 @@ func (a *authData) next() *authData { a.mutex.Lock() defer a.mutex.Unlock() if a.connectionID > 0xff000000 || a.connectionID == 0 { - fastrand.Read(a.clientID[:]) - a.connectionID = fastrand.Uint32() & 0xffffff + rand.Read(a.clientID[:]) + a.connectionID = randv2.Uint32() & 0xffffff } a.connectionID++ copy(r.clientID[:], a.clientID[:]) diff --git a/transport/ssr/protocol/protocol.go b/transport/ssr/protocol/protocol.go index a04e6bd4..ad6bf6ba 100644 --- a/transport/ssr/protocol/protocol.go +++ b/transport/ssr/protocol/protocol.go @@ -8,7 +8,7 @@ import ( N "github.com/metacubex/mihomo/common/net" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) var ( @@ -71,7 +71,7 @@ func getHeadSize(b []byte, defaultValue int) int { func getDataLength(b []byte) int { bLength := len(b) - dataLength := getHeadSize(b, 30) + fastrand.Intn(32) + dataLength := getHeadSize(b, 30) + randv2.IntN(32) if bLength < dataLength { return bLength } diff --git a/transport/tuic/congestion/bbr_sender.go b/transport/tuic/congestion/bbr_sender.go index 6cea9355..5a863362 100644 --- a/transport/tuic/congestion/bbr_sender.go +++ b/transport/tuic/congestion/bbr_sender.go @@ -9,7 +9,7 @@ import ( "github.com/metacubex/quic-go" "github.com/metacubex/quic-go/congestion" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) const ( @@ -716,7 +716,7 @@ func (b *bbrSender) EnterProbeBandwidthMode(now time.Time) { // Pick a random offset for the gain cycle out of {0, 2..7} range. 1 is // excluded because in that case increased gain and decreased gain would not // follow each other. - b.cycleCurrentOffset = fastrand.Int() % (GainCycleLength - 1) + b.cycleCurrentOffset = randv2.Int() % (GainCycleLength - 1) if b.cycleCurrentOffset >= 1 { b.cycleCurrentOffset += 1 } diff --git a/transport/tuic/congestion_v2/bbr_sender.go b/transport/tuic/congestion_v2/bbr_sender.go index 54705978..ed29bd7e 100644 --- a/transport/tuic/congestion_v2/bbr_sender.go +++ b/transport/tuic/congestion_v2/bbr_sender.go @@ -9,7 +9,7 @@ import ( "github.com/metacubex/quic-go" "github.com/metacubex/quic-go/congestion" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) // BbrSender implements BBR congestion control algorithm. BBR aims to estimate @@ -620,7 +620,7 @@ func (b *bbrSender) enterProbeBandwidthMode(now time.Time) { // Pick a random offset for the gain cycle out of {0, 2..7} range. 1 is // excluded because in that case increased gain and decreased gain would not // follow each other. - b.cycleCurrentOffset = int(fastrand.Int31n(congestion.PacketsPerConnectionID)) % (gainCycleLength - 1) + b.cycleCurrentOffset = int(randv2.Int32N(congestion.PacketsPerConnectionID)) % (gainCycleLength - 1) if b.cycleCurrentOffset >= 1 { b.cycleCurrentOffset += 1 } diff --git a/transport/tuic/v4/client.go b/transport/tuic/v4/client.go index 5c9c889c..62b419b7 100644 --- a/transport/tuic/v4/client.go +++ b/transport/tuic/v4/client.go @@ -20,8 +20,8 @@ import ( "github.com/metacubex/mihomo/transport/tuic/common" "github.com/metacubex/quic-go" + "github.com/metacubex/randv2" "github.com/puzpuzpuz/xsync/v3" - "github.com/zhangyunhao116/fastrand" ) type ClientOption struct { @@ -367,7 +367,7 @@ func (t *clientImpl) ListenPacketWithDialer(ctx context.Context, metadata *C.Met pipe1, pipe2 := N.Pipe() var connId uint32 for { - connId = fastrand.Uint32() + connId = randv2.Uint32() _, loaded := t.udpInputMap.LoadOrStore(connId, pipe1) if !loaded { break diff --git a/transport/tuic/v5/client.go b/transport/tuic/v5/client.go index 89454add..a3c13d2b 100644 --- a/transport/tuic/v5/client.go +++ b/transport/tuic/v5/client.go @@ -20,8 +20,8 @@ import ( "github.com/metacubex/mihomo/transport/tuic/common" "github.com/metacubex/quic-go" + "github.com/metacubex/randv2" "github.com/puzpuzpuz/xsync/v3" - "github.com/zhangyunhao116/fastrand" ) type ClientOption struct { @@ -351,7 +351,7 @@ func (t *clientImpl) ListenPacketWithDialer(ctx context.Context, metadata *C.Met pipe1, pipe2 := N.Pipe() var connId uint16 for { - connId = uint16(fastrand.Intn(0xFFFF)) + connId = uint16(randv2.IntN(0xFFFF)) _, loaded := t.udpInputMap.LoadOrStore(connId, pipe1) if !loaded { break diff --git a/transport/tuic/v5/packet.go b/transport/tuic/v5/packet.go index 5608db81..8281a11e 100644 --- a/transport/tuic/v5/packet.go +++ b/transport/tuic/v5/packet.go @@ -12,7 +12,7 @@ import ( "github.com/metacubex/mihomo/transport/tuic/common" "github.com/metacubex/quic-go" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) type quicStreamPacketConn struct { @@ -157,7 +157,7 @@ func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err erro if err != nil { return } - pktId := uint16(fastrand.Uint32()) + pktId := uint16(randv2.Uint32()) packet := NewPacket(q.connId, pktId, 1, 0, uint16(len(p)), address, p) switch q.udpRelayMode { case common.QUIC: diff --git a/transport/vless/vision/padding.go b/transport/vless/vision/padding.go index e5f9dc85..dd9cb261 100644 --- a/transport/vless/vision/padding.go +++ b/transport/vless/vision/padding.go @@ -8,7 +8,7 @@ import ( "github.com/metacubex/mihomo/log" "github.com/gofrs/uuid/v5" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) const ( @@ -25,9 +25,9 @@ func WriteWithPadding(buffer *buf.Buffer, p []byte, command byte, userUUID *uuid if contentLen < 900 { if paddingTLS { //log.Debugln("long padding") - paddingLen = fastrand.Int31n(500) + 900 - contentLen + paddingLen = randv2.Int32N(500) + 900 - contentLen } else { - paddingLen = fastrand.Int31n(256) + paddingLen = randv2.Int32N(256) } } if userUUID != nil { @@ -49,9 +49,9 @@ func ApplyPadding(buffer *buf.Buffer, command byte, userUUID *uuid.UUID, padding if contentLen < 900 { if paddingTLS { //log.Debugln("long padding") - paddingLen = fastrand.Int31n(500) + 900 - contentLen + paddingLen = randv2.Int32N(500) + 900 - contentLen } else { - paddingLen = fastrand.Int31n(256) + paddingLen = randv2.Int32N(256) } } diff --git a/transport/vmess/conn.go b/transport/vmess/conn.go index 292137ab..b65a447d 100644 --- a/transport/vmess/conn.go +++ b/transport/vmess/conn.go @@ -6,6 +6,7 @@ import ( "crypto/cipher" "crypto/hmac" "crypto/md5" + "crypto/rand" "crypto/sha256" "encoding/binary" "errors" @@ -14,7 +15,7 @@ import ( "net" "time" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" "golang.org/x/crypto/chacha20poly1305" ) @@ -72,7 +73,7 @@ func (vc *Conn) sendRequest() error { buf.WriteByte(vc.respV) buf.WriteByte(OptionChunkStream) - p := fastrand.Intn(16) + p := randv2.IntN(16) // P Sec Reserve Cmd buf.WriteByte(byte(p<<4) | byte(vc.security)) buf.WriteByte(0) @@ -90,7 +91,7 @@ func (vc *Conn) sendRequest() error { // padding if p > 0 { padding := make([]byte, p) - fastrand.Read(padding) + rand.Read(padding) buf.Write(padding) } @@ -196,7 +197,7 @@ func hashTimestamp(t time.Time) []byte { // newConn return a Conn instance func newConn(conn net.Conn, id *ID, dst *DstAddr, security Security, isAead bool) (*Conn, error) { randBytes := make([]byte, 33) - fastrand.Read(randBytes) + rand.Read(randBytes) reqBodyIV := make([]byte, 16) reqBodyKey := make([]byte, 16) copy(reqBodyIV[:], randBytes[:16]) diff --git a/transport/vmess/h2.go b/transport/vmess/h2.go index f91c2766..a39ec5d9 100644 --- a/transport/vmess/h2.go +++ b/transport/vmess/h2.go @@ -7,7 +7,7 @@ import ( "net/http" "net/url" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" "golang.org/x/net/http2" ) @@ -27,7 +27,7 @@ type H2Config struct { func (hc *h2Conn) establishConn() error { preader, pwriter := io.Pipe() - host := hc.cfg.Hosts[fastrand.Intn(len(hc.cfg.Hosts))] + host := hc.cfg.Hosts[randv2.IntN(len(hc.cfg.Hosts))] path := hc.cfg.Path // TODO: connect use VMess Host instead of H2 Host req := http.Request{ diff --git a/transport/vmess/http.go b/transport/vmess/http.go index 4a1b93ec..3c66fe6b 100644 --- a/transport/vmess/http.go +++ b/transport/vmess/http.go @@ -11,7 +11,7 @@ import ( "github.com/metacubex/mihomo/common/utils" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) type httpConn struct { @@ -59,10 +59,10 @@ func (hc *httpConn) Write(b []byte) (int, error) { return -1, errors.New("path is empty") } - path := hc.cfg.Path[fastrand.Intn(len(hc.cfg.Path))] + path := hc.cfg.Path[randv2.IntN(len(hc.cfg.Path))] host := hc.cfg.Host if header := hc.cfg.Headers["Host"]; len(header) != 0 { - host = header[fastrand.Intn(len(header))] + host = header[randv2.IntN(len(header))] } u := fmt.Sprintf("http://%s%s", net.JoinHostPort(host, "80"), path) @@ -71,7 +71,7 @@ func (hc *httpConn) Write(b []byte) (int, error) { return 0, err } for key, list := range hc.cfg.Headers { - req.Header.Set(key, list[fastrand.Intn(len(list))]) + req.Header.Set(key, list[randv2.IntN(len(list))]) } req.ContentLength = int64(len(b)) if err := req.Write(hc.Conn); err != nil { diff --git a/transport/vmess/vmess.go b/transport/vmess/vmess.go index 7c587c6a..22c77ab7 100644 --- a/transport/vmess/vmess.go +++ b/transport/vmess/vmess.go @@ -8,7 +8,7 @@ import ( "github.com/metacubex/mihomo/common/utils" "github.com/gofrs/uuid/v5" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) // Version of vmess @@ -78,7 +78,7 @@ type Config struct { // StreamConn return a Conn with net.Conn and DstAddr func (c *Client) StreamConn(conn net.Conn, dst *DstAddr) (net.Conn, error) { - r := fastrand.Intn(len(c.user)) + r := randv2.IntN(len(c.user)) return newConn(conn, c.user[r], dst, c.security, c.isAead) } diff --git a/transport/vmess/websocket.go b/transport/vmess/websocket.go index f6914199..ca3a57b7 100644 --- a/transport/vmess/websocket.go +++ b/transport/vmess/websocket.go @@ -4,6 +4,7 @@ import ( "bufio" "bytes" "context" + "crypto/rand" "crypto/sha1" "crypto/tls" "encoding/base64" @@ -25,7 +26,7 @@ import ( "github.com/gobwas/ws" "github.com/gobwas/ws/wsutil" - "github.com/zhangyunhao116/fastrand" + "github.com/metacubex/randv2" ) type websocketConn struct { @@ -150,7 +151,7 @@ func (wsc *websocketConn) WriteBuffer(buffer *buf.Buffer) error { } if wsc.state.ClientSide() { - maskKey := fastrand.Uint32() + maskKey := randv2.Uint32() binary.LittleEndian.PutUint32(header[1+payloadBitLength:], maskKey) N.MaskWebSocket(maskKey, data) } @@ -398,7 +399,7 @@ func streamWebsocketConn(ctx context.Context, conn net.Conn, c *WebsocketConfig, const nonceKeySize = 16 // NOTE: bts does not escape. bts := make([]byte, nonceKeySize) - if _, err = fastrand.Read(bts); err != nil { + if _, err = rand.Read(bts); err != nil { return nil, fmt.Errorf("rand read error: %w", err) } secKey = base64.StdEncoding.EncodeToString(bts) From 59fd3cffe3767b51428cfd70473e4c295aa88089 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Fri, 31 May 2024 17:02:31 +0800 Subject: [PATCH 14/38] ci: fix arm package create --- .github/workflows/build.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 42023755..9b8e000a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -206,6 +206,10 @@ jobs: sudo apt-get install dpkg if [ "${{matrix.jobs.abi}}" = "1" ]; then ARCH=loongarch64 + elif [ "${{matrix.jobs.goarm}}" = "7" ]; then + ARCH=armhf + elif [ "${{matrix.jobs.goarch}}" = "arm" ]; then + ARCH=armel else ARCH=${{matrix.jobs.goarch}} fi From be3d121ec633a0665d31fc66bff3707893910bcb Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 1 Jun 2024 13:34:02 +0800 Subject: [PATCH 15/38] fix: darwin calculate correct tunIndex --- listener/sing_tun/server.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 09bf308c..a5edb77f 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -24,6 +24,7 @@ import ( E "github.com/sagernet/sing/common/exceptions" F "github.com/sagernet/sing/common/format" "github.com/sagernet/sing/common/ranges" + "golang.org/x/exp/slices" ) var InterfaceName = "Meta" @@ -60,19 +61,21 @@ func CalculateInterfaceName(name string) (tunName string) { return } tunIndex := 0 - indexSet := make(map[int]struct{}) + indexArr := make([]int, 0, len(interfaces)) for _, netInterface := range interfaces { if strings.HasPrefix(netInterface.Name, tunName) { index, parseErr := strconv.ParseInt(netInterface.Name[len(tunName):], 10, 16) if parseErr == nil { - indexSet[int(index)] = struct{}{} + indexArr = append(indexArr, int(index)) } } } - for index := range indexSet { + slices.Sort(indexArr) + indexArr = slices.Compact(indexArr) + for _, index := range indexArr { if index == tunIndex { tunIndex += 1 - } else { // indexSet already sorted and distinct, so this tunIndex nobody used + } else { // indexArr already sorted and distinct, so this tunIndex nobody used break } } From 7b3c9e94e6bfb138b241e47eb83c5d019cd1e728 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sun, 2 Jun 2024 02:36:15 +0800 Subject: [PATCH 16/38] chore: Better package name handling on Android --- component/process/process_linux.go | 54 +++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/component/process/process_linux.go b/component/process/process_linux.go index f8174495..4667104c 100644 --- a/component/process/process_linux.go +++ b/component/process/process_linux.go @@ -2,19 +2,23 @@ package process import ( "bytes" + "context" "encoding/binary" "fmt" "net/netip" "os" - "path" "path/filepath" "runtime" "strings" + "sync" "syscall" "unicode" "unsafe" + "github.com/metacubex/mihomo/log" + "github.com/mdlayher/netlink" + tun "github.com/metacubex/sing-tun" "golang.org/x/sys/unix" ) @@ -59,6 +63,19 @@ type inetDiagResponse struct { INode uint32 } +type MyCallback struct{} + +var ( + packageManager tun.PackageManager + once sync.Once +) + +func (cb *MyCallback) OnPackagesUpdated(packageCount int, sharedCount int) {} + +func (cb *MyCallback) NewError(ctx context.Context, err error) { + log.Warnln("%s", err) +} + func findProcessName(network string, ip netip.Addr, srcPort int) (uint32, string, error) { uid, inode, err := resolveSocketByNetlink(network, ip, srcPort) if err != nil { @@ -162,12 +179,7 @@ func resolveProcessNameByProcSearch(inode, uid uint32) (string, error) { } if runtime.GOOS == "android" { if bytes.Equal(buffer[:n], socket) { - cmdline, err := os.ReadFile(path.Join(processPath, "cmdline")) - if err != nil { - return "", err - } - - return splitCmdline(cmdline), nil + return findPackageName(uid), nil } } else { if bytes.Equal(buffer[:n], socket) { @@ -181,17 +193,29 @@ func resolveProcessNameByProcSearch(inode, uid uint32) (string, error) { return "", fmt.Errorf("process of uid(%d),inode(%d) not found", uid, inode) } -func splitCmdline(cmdline []byte) string { - cmdline = bytes.Trim(cmdline, " ") - - idx := bytes.IndexFunc(cmdline, func(r rune) bool { - return unicode.IsControl(r) || unicode.IsSpace(r) || r == ':' +func findPackageName(uid uint32) string { + once.Do(func() { + callback := &MyCallback{} + var err error + packageManager, err = tun.NewPackageManager(callback) + if err != nil { + log.Warnln("%s", err) + } + err = packageManager.Start() + if err != nil { + log.Warnln("%s", err) + return + } }) - if idx == -1 { - return filepath.Base(string(cmdline)) + if sharedPackage, loaded := packageManager.SharedPackageByID(uid % 100000); loaded { + fmt.Println(loaded) + return sharedPackage } - return filepath.Base(string(cmdline[:idx])) + if packageName, loaded := packageManager.PackageByID(uid % 100000); loaded { + return packageName + } + return "" } func isPid(s string) bool { From 063836fe5dd3add744337b64512d41f2d12bd93a Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 5 Jun 2024 11:56:11 +0800 Subject: [PATCH 17/38] chore: sync hysteria2 bbr changes https://github.com/apernet/hysteria/commit/e0e75c46309eec2531b8613370cfbada259b5af3 --- transport/tuic/congestion_v2/bbr_sender.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/transport/tuic/congestion_v2/bbr_sender.go b/transport/tuic/congestion_v2/bbr_sender.go index ed29bd7e..d8852fbc 100644 --- a/transport/tuic/congestion_v2/bbr_sender.go +++ b/transport/tuic/congestion_v2/bbr_sender.go @@ -62,7 +62,7 @@ const ( // Flag. defaultStartupFullLossCount = 8 quicBbr2DefaultLossThreshold = 0.02 - maxBbrBurstPackets = 3 + maxBbrBurstPackets = 10 ) type bbrMode int @@ -334,6 +334,8 @@ func (b *bbrSender) OnPacketSent( } b.sampler.OnPacketSent(sentTime, packetNumber, bytes, bytesInFlight, isRetransmittable) + + b.maybeAppLimited(bytesInFlight) } // CanSend implements the SendAlgorithm interface. @@ -413,8 +415,6 @@ func (b *bbrSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, even // packet in lost_packets. var lastPacketSendState sendTimeState - b.maybeApplimited(priorInFlight) - // Update bytesInFlight b.bytesInFlight = priorInFlight for _, p := range ackedPackets { @@ -541,7 +541,7 @@ func (b *bbrSender) setDrainGain(drainGain float64) { b.drainGain = drainGain } -// What's the current estimated bandwidth in bytes per second. +// Get the current bandwidth estimate. Note that Bandwidth is in bits per second. func (b *bbrSender) bandwidthEstimate() Bandwidth { return b.maxBandwidth.GetBest() } @@ -700,14 +700,13 @@ func (b *bbrSender) checkIfFullBandwidthReached(lastPacketSendState *sendTimeSta } } -func (b *bbrSender) maybeApplimited(bytesInFlight congestion.ByteCount) { +func (b *bbrSender) maybeAppLimited(bytesInFlight congestion.ByteCount) { congestionWindow := b.GetCongestionWindow() if bytesInFlight >= congestionWindow { return } availableBytes := congestionWindow - bytesInFlight - drainLimited := b.mode == bbrModeDrain && bytesInFlight > congestionWindow/2 - if !drainLimited || availableBytes > maxBbrBurstPackets*b.maxDatagramSize { + if availableBytes > maxBbrBurstPackets*b.maxDatagramSize { b.sampler.OnAppLimited() } } From 0d4e57cb21a1b6fe59bdba2833dad8cef630b335 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 7 Jun 2024 19:17:01 +0800 Subject: [PATCH 18/38] chore: update quic-go to 0.45.0 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index bc32a633..a6664b59 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.44.1-0.20240521004242-fcd70d587e22 + github.com/metacubex/quic-go v0.45.1-0.20240607133845-b24f02b35a22 github.com/metacubex/randv2 v0.2.0 github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.6 diff --git a/go.sum b/go.sum index 6dbbfd77..c75275bc 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-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.44.1-0.20240521004242-fcd70d587e22 h1:hsQ0b2A509b6ubnLtLOcUgZ8vOb+d/667zVEJ1T2fao= -github.com/metacubex/quic-go v0.44.1-0.20240521004242-fcd70d587e22/go.mod h1:88wAATpevav4xdy5N8oejQ2cbbI6EcLYEklFeo+qywA= +github.com/metacubex/quic-go v0.45.1-0.20240607133845-b24f02b35a22 h1:dKYoWnrB5bbCMoMQit4INUDKiDcjc0Azsm3GltYf9Pw= +github.com/metacubex/quic-go v0.45.1-0.20240607133845-b24f02b35a22/go.mod h1:Yza2H7Ax1rxWPUcJx0vW+oAt9EsPuSiyQFhFabUPzwU= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1 h1:7hDHLTmjgtRoAp59STwPQpe5Pinwi4cWex+FB3Ohvco= From cacfefad4b5925b07d2340bcdee1240197f19d12 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 10 Jun 2024 08:48:23 +0800 Subject: [PATCH 19/38] fix: quic-go cached dial error --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a6664b59..5d109d8d 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.45.1-0.20240607133845-b24f02b35a22 + github.com/metacubex/quic-go v0.45.1-0.20240610004319-163fee60637e github.com/metacubex/randv2 v0.2.0 github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.6 diff --git a/go.sum b/go.sum index c75275bc..5c8219c2 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-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.45.1-0.20240607133845-b24f02b35a22 h1:dKYoWnrB5bbCMoMQit4INUDKiDcjc0Azsm3GltYf9Pw= -github.com/metacubex/quic-go v0.45.1-0.20240607133845-b24f02b35a22/go.mod h1:Yza2H7Ax1rxWPUcJx0vW+oAt9EsPuSiyQFhFabUPzwU= +github.com/metacubex/quic-go v0.45.1-0.20240610004319-163fee60637e h1:bLYn3GuRvWDcBDAkIv5kUYIhzHwafDVq635BuybnKqI= +github.com/metacubex/quic-go v0.45.1-0.20240610004319-163fee60637e/go.mod h1:Yza2H7Ax1rxWPUcJx0vW+oAt9EsPuSiyQFhFabUPzwU= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1 h1:7hDHLTmjgtRoAp59STwPQpe5Pinwi4cWex+FB3Ohvco= From 10f8ba44345e24f26ec63bab8919fc287de7196d Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Wed, 12 Jun 2024 04:46:13 +0800 Subject: [PATCH 20/38] chore: Disable the loop back detector for CMFA --- adapter/outbound/direct.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go index 1b01a576..09b9696b 100644 --- a/adapter/outbound/direct.go +++ b/adapter/outbound/direct.go @@ -4,14 +4,19 @@ import ( "context" "errors" "net/netip" + "os" + "strconv" N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/loopback" "github.com/metacubex/mihomo/component/resolver" C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/constant/features" ) +var DisableLoopBackDetector, _ = strconv.ParseBool(os.Getenv("DISABLE_LOOPBACK_DETECTOR")) + type Direct struct { *Base loopBack *loopback.Detector @@ -24,8 +29,10 @@ type DirectOption struct { // DialContext implements C.ProxyAdapter func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { - if err := d.loopBack.CheckConn(metadata); err != nil { - return nil, err + if !features.CMFA && !DisableLoopBackDetector { + if err := d.loopBack.CheckConn(metadata); err != nil { + return nil, err + } } opts = append(opts, dialer.WithResolver(resolver.DefaultResolver)) c, err := dialer.DialContext(ctx, "tcp", metadata.RemoteAddress(), d.Base.DialOptions(opts...)...) @@ -38,8 +45,10 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ... // ListenPacketContext implements C.ProxyAdapter func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { - if err := d.loopBack.CheckPacketConn(metadata); err != nil { - return nil, err + if !features.CMFA && !DisableLoopBackDetector { + if err := d.loopBack.CheckPacketConn(metadata); err != nil { + return nil, err + } } // net.UDPConn.WriteTo only working with *net.UDPAddr, so we need a net.UDPAddr if !metadata.Resolved() { From 5678131591afc4a7f60c1c7b6e34f189cf7ada22 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 12 Jun 2024 11:37:09 +0800 Subject: [PATCH 21/38] fix: wireguard server resolve when only a server in `peers` --- adapter/outbound/wireguard.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 976f3959..56ade71c 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -304,13 +304,16 @@ func (w *WireGuard) init(ctx context.Context) error { ipcConf := "private_key=" + w.option.PrivateKey if len(w.option.Peers) > 0 { for i, peer := range w.option.Peers { + ipcConf += "\npublic_key=" + peer.PublicKey destination, err := w.resolve(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 + if len(w.option.Peers) == 1 { // must call SetConnectAddr if isConnect == true + w.bind.SetConnectAddr(destination) + } ipcConf += "\nendpoint=" + destination.String() if peer.PreSharedKey != "" { ipcConf += "\npreshared_key=" + peer.PreSharedKey @@ -332,7 +335,7 @@ func (w *WireGuard) init(ctx context.Context) error { // let us can retry domain resolve in next time return E.Cause(err, "resolve endpoint domain") } - w.bind.SetConnectAddr(destination) + w.bind.SetConnectAddr(destination) // must call SetConnectAddr if isConnect == true ipcConf += "\nendpoint=" + destination.String() if w.option.PreSharedKey != "" { ipcConf += "\npreshared_key=" + w.option.PreSharedKey From f317baa8def8bbcdef0fa8259d12d8181ec8fe19 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 12 Jun 2024 15:25:34 +0800 Subject: [PATCH 22/38] feat: add `respect-rules` for dns --- config/config.go | 27 +++++++---- dns/util.go | 118 +++++++++++++++++++++++++++-------------------- docs/config.yaml | 10 +++- tunnel/tunnel.go | 6 +-- 4 files changed, 98 insertions(+), 63 deletions(-) diff --git a/config/config.go b/config/config.go index 74a2053e..fd12d2db 100644 --- a/config/config.go +++ b/config/config.go @@ -212,6 +212,7 @@ type RawDNS struct { IPv6Timeout uint `yaml:"ipv6-timeout" json:"ipv6-timeout"` UseHosts bool `yaml:"use-hosts" json:"use-hosts"` UseSystemHosts bool `yaml:"use-system-hosts" json:"use-system-hosts"` + RespectRules bool `yaml:"respect-rules" json:"respect-rules"` NameServer []string `yaml:"nameserver" json:"nameserver"` Fallback []string `yaml:"fallback" json:"fallback"` FallbackFilter RawFallbackFilter `yaml:"fallback-filter" json:"fallback-filter"` @@ -1039,7 +1040,7 @@ func hostWithDefaultPort(host string, defPort string) (string, error) { return net.JoinHostPort(hostname, port), nil } -func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error) { +func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns.NameServer, error) { var nameservers []dns.NameServer for idx, server := range servers { @@ -1114,6 +1115,10 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error) return nil, fmt.Errorf("DNS NameServer[%d] format error: %s", idx, err.Error()) } + if respectRules && len(proxyName) == 0 { + proxyName = dns.RespectRules + } + nameservers = append( nameservers, dns.NameServer{ @@ -1130,7 +1135,7 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error) func init() { dns.ParseNameServer = func(servers []string) ([]dns.NameServer, error) { // using by wireguard - return parseNameServer(servers, false) + return parseNameServer(servers, false, false) } } @@ -1156,7 +1161,7 @@ func parsePureDNSServer(server string) string { } } } -func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], ruleProviders map[string]providerTypes.RuleProvider, preferH3 bool) (*orderedmap.OrderedMap[string, []dns.NameServer], error) { +func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], ruleProviders map[string]providerTypes.RuleProvider, respectRules bool, preferH3 bool) (*orderedmap.OrderedMap[string, []dns.NameServer], error) { policy := orderedmap.New[string, []dns.NameServer]() updatedPolicy := orderedmap.New[string, any]() re := regexp.MustCompile(`[a-zA-Z0-9\-]+\.[a-zA-Z]{2,}(\.[a-zA-Z]{2,})?`) @@ -1202,7 +1207,7 @@ func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], rulePro if err != nil { return nil, err } - nameservers, err := parseNameServer(servers, preferH3) + nameservers, err := parseNameServer(servers, respectRules, preferH3) if err != nil { return nil, err } @@ -1296,6 +1301,10 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul return nil, fmt.Errorf("if DNS configuration is turned on, NameServer cannot be empty") } + if cfg.RespectRules && len(cfg.ProxyServerNameserver) == 0 { + return nil, fmt.Errorf("if “respect-rules” is turned on, “proxy-server-nameserver” cannot be empty") + } + dnsCfg := &DNS{ Enable: cfg.Enable, Listen: cfg.Listen, @@ -1310,26 +1319,26 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul }, } var err error - if dnsCfg.NameServer, err = parseNameServer(cfg.NameServer, cfg.PreferH3); err != nil { + if dnsCfg.NameServer, err = parseNameServer(cfg.NameServer, cfg.RespectRules, cfg.PreferH3); err != nil { return nil, err } - if dnsCfg.Fallback, err = parseNameServer(cfg.Fallback, cfg.PreferH3); err != nil { + if dnsCfg.Fallback, err = parseNameServer(cfg.Fallback, cfg.RespectRules, cfg.PreferH3); err != nil { return nil, err } - if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy, ruleProviders, cfg.PreferH3); err != nil { + if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy, ruleProviders, cfg.RespectRules, cfg.PreferH3); err != nil { return nil, err } - if dnsCfg.ProxyServerNameserver, err = parseNameServer(cfg.ProxyServerNameserver, cfg.PreferH3); err != nil { + if dnsCfg.ProxyServerNameserver, err = parseNameServer(cfg.ProxyServerNameserver, false, cfg.PreferH3); err != nil { return nil, err } if len(cfg.DefaultNameserver) == 0 { return nil, errors.New("default nameserver should have at least one nameserver") } - if dnsCfg.DefaultNameserver, err = parseNameServer(cfg.DefaultNameserver, cfg.PreferH3); err != nil { + if dnsCfg.DefaultNameserver, err = parseNameServer(cfg.DefaultNameserver, false, cfg.PreferH3); err != nil { return nil, err } // check default nameserver is pure ip addr diff --git a/dns/util.go b/dns/util.go index 516c63fb..9b84a507 100644 --- a/dns/util.go +++ b/dns/util.go @@ -7,7 +7,6 @@ import ( "fmt" "net" "net/netip" - "strconv" "strings" "time" @@ -175,6 +174,8 @@ func msgToDomain(msg *D.Msg) string { return "" } +const RespectRules = "RULES" + type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error) func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) dialHandler { @@ -183,54 +184,67 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts = append(opts, dialer.WithResolver(r)) return dialer.DialContext(ctx, network, addr, opts...) } else { - host, port, err := net.SplitHostPort(addr) + metadata := &C.Metadata{ + NetWork: C.TCP, + Type: C.INNER, + } + err := metadata.SetRemoteAddress(addr) // tcp can resolve host by remote if err != nil { return nil, err } - uintPort, err := strconv.ParseUint(port, 10, 16) - if err != nil { - return nil, err + if !strings.Contains(network, "tcp") { + metadata.NetWork = C.UDP + if !metadata.Resolved() { + // udp must resolve host first + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) + if err != nil { + return nil, err + } + metadata.DstIP = dstIP + } } + if proxyAdapter == nil { - var ok bool - proxyAdapter, ok = tunnel.Proxies()[proxyName] - if !ok { - opts = append(opts, dialer.WithInterface(proxyName)) + if proxyName == RespectRules { + if !metadata.Resolved() { + // resolve here before ResolveMetadata to avoid its inner resolver.ResolveIP + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) + if err != nil { + return nil, err + } + metadata.DstIP = dstIP + } + proxyAdapter, _, err = tunnel.ResolveMetadata(metadata) + if err != nil { + return nil, err + } + } else { + var ok bool + proxyAdapter, ok = tunnel.Proxies()[proxyName] + if !ok { + opts = append(opts, dialer.WithInterface(proxyName)) + } } } if strings.Contains(network, "tcp") { - // tcp can resolve host by remote - metadata := &C.Metadata{ - NetWork: C.TCP, - Host: host, - DstPort: uint16(uintPort), - } if proxyAdapter != nil { if proxyAdapter.IsL3Protocol(metadata) { // L3 proxy should resolve domain before to avoid loopback - dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r) - if err != nil { - return nil, err + if !metadata.Resolved() { + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) + if err != nil { + return nil, err + } + metadata.DstIP = dstIP } - metadata.Host = "" - metadata.DstIP = dstIP + metadata.Host = "" // clear host to avoid double resolve in proxy } + log.Debugln("%s", metadata.RemoteAddress()) return proxyAdapter.DialContext(ctx, metadata, opts...) } opts = append(opts, dialer.WithResolver(r)) return dialer.DialContext(ctx, network, addr, opts...) } else { - // udp must resolve host first - dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r) - if err != nil { - return nil, err - } - metadata := &C.Metadata{ - NetWork: C.UDP, - Host: "", - DstIP: dstIP, - DstPort: uint16(uintPort), - } if proxyAdapter == nil { return dialer.DialContext(ctx, network, addr, opts...) } @@ -251,33 +265,37 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, } func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.PacketConn, error) { - host, port, err := net.SplitHostPort(addr) + metadata := &C.Metadata{ + NetWork: C.UDP, + } + err := metadata.SetRemoteAddress(addr) if err != nil { return nil, err } - uintPort, err := strconv.ParseUint(port, 10, 16) - if err != nil { - return nil, err + if !metadata.Resolved() { + // udp must resolve host first + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) + if err != nil { + return nil, err + } + metadata.DstIP = dstIP } + if proxyAdapter == nil { - var ok bool - proxyAdapter, ok = tunnel.Proxies()[proxyName] - if !ok { - opts = append(opts, dialer.WithInterface(proxyName)) + if proxyName == RespectRules { + proxyAdapter, _, err = tunnel.ResolveMetadata(metadata) + if err != nil { + return nil, err + } + } else { + var ok bool + proxyAdapter, ok = tunnel.Proxies()[proxyName] + if !ok { + opts = append(opts, dialer.WithInterface(proxyName)) + } } } - // udp must resolve host first - dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r) - if err != nil { - return nil, err - } - metadata := &C.Metadata{ - NetWork: C.UDP, - Host: "", - DstIP: dstIP, - DstPort: uint16(uintPort), - } if proxyAdapter == nil { return dialer.NewDialer(opts...).ListenPacket(ctx, network, "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort)) } diff --git a/docs/config.yaml b/docs/config.yaml index fe850163..bd263c14 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -209,7 +209,7 @@ tunnels: # one line config dns: cache-algorithm: arc enable: false # 关闭将使用系统 DNS - prefer-h3: true # 开启 DoH 支持 HTTP/3,将并发尝试 + prefer-h3: false # 是否开启 DoH 支持 HTTP/3,将并发尝试 listen: 0.0.0.0:53 # 开启 DNS 服务器监听 # ipv6: false # false 将返回 AAAA 的空结果 # ipv6-timeout: 300 # 单位:ms,内部双栈并发时,向上游查询 AAAA 时,等待 AAAA 的时间,默认 100ms @@ -227,6 +227,13 @@ dns: # use-hosts: true # 查询 hosts + # 配置后面的nameserver、fallback和nameserver-policy向dns服务器的连接过程是否遵守遵守rules规则 + # 如果为false(默认值)则这三部分的dns服务器在未特别指定的情况下会直连 + # 如果为true,将会按照rules的规则匹配链接方式(走代理或直连),如果有特别指定则任然以指定值为准 + # 仅当proxy-server-nameserver非空时可以开启此选项, 强烈不建议和prefer-h3一起使用 + # 此外,这三者配置中的dns服务器如果出现域名会采用default-nameserver配置项解析,也请确保正确配置default-nameserver + respect-rules: false + # 配置不使用 fake-ip 的域名 # fake-ip-filter: # - '*.lan' @@ -244,6 +251,7 @@ dns: - https://mozilla.cloudflare-dns.com/dns-query#DNS&h3=true # 指定策略组和使用 HTTP/3 - dhcp://en0 # dns from dhcp - quic://dns.adguard.com:784 # DNS over QUIC + # - '8.8.8.8#RULES' # 效果同respect-rules,但仅对该服务器生效 # - '8.8.8.8#en0' # 兼容指定 DNS 出口网卡 # 当配置 fallback 时,会查询 nameserver 中返回的 IP 是否为 CN,非必要配置 diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 608ab2c5..78628a56 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -278,7 +278,7 @@ func preHandleMetadata(metadata *C.Metadata) error { return nil } -func resolveMetadata(metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err error) { +func ResolveMetadata(metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err error) { if metadata.SpecialProxy != "" { var exist bool proxy, exist = proxies[metadata.SpecialProxy] @@ -375,7 +375,7 @@ func handleUDPConn(packet C.PacketAdapter) { cond.Broadcast() }() - proxy, rule, err := resolveMetadata(metadata) + proxy, rule, err := ResolveMetadata(metadata) if err != nil { log.Warnln("[UDP] Parse metadata failed: %s", err.Error()) return @@ -486,7 +486,7 @@ func handleTCPConn(connCtx C.ConnContext) { }() } - proxy, rule, err := resolveMetadata(metadata) + proxy, rule, err := ResolveMetadata(metadata) if err != nil { log.Warnln("[Metadata] parse failed: %s", err.Error()) return From 2b4741fbc71c1cc8ed1e4e94b65288337daf880b Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 12 Jun 2024 17:09:21 +0800 Subject: [PATCH 23/38] chore: add inner dns proxied connection statistic to restful api --- dns/util.go | 56 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/dns/util.go b/dns/util.go index 9b84a507..f13279fa 100644 --- a/dns/util.go +++ b/dns/util.go @@ -18,6 +18,7 @@ import ( C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" "github.com/metacubex/mihomo/tunnel" + "github.com/metacubex/mihomo/tunnel/statistic" D "github.com/miekg/dns" "github.com/samber/lo" @@ -204,6 +205,7 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, } } + var rule C.Rule if proxyAdapter == nil { if proxyName == RespectRules { if !metadata.Resolved() { @@ -214,7 +216,7 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, } metadata.DstIP = dstIP } - proxyAdapter, _, err = tunnel.ResolveMetadata(metadata) + proxyAdapter, rule, err = tunnel.ResolveMetadata(metadata) if err != nil { return nil, err } @@ -228,22 +230,30 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, } if strings.Contains(network, "tcp") { - if proxyAdapter != nil { - if proxyAdapter.IsL3Protocol(metadata) { // L3 proxy should resolve domain before to avoid loopback - if !metadata.Resolved() { - dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) - if err != nil { - return nil, err - } - metadata.DstIP = dstIP - } - metadata.Host = "" // clear host to avoid double resolve in proxy - } - log.Debugln("%s", metadata.RemoteAddress()) - return proxyAdapter.DialContext(ctx, metadata, opts...) + if proxyAdapter == nil { + opts = append(opts, dialer.WithResolver(r)) + return dialer.DialContext(ctx, network, addr, opts...) } - opts = append(opts, dialer.WithResolver(r)) - return dialer.DialContext(ctx, network, addr, opts...) + + if proxyAdapter.IsL3Protocol(metadata) { // L3 proxy should resolve domain before to avoid loopback + if !metadata.Resolved() { + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) + if err != nil { + return nil, err + } + metadata.DstIP = dstIP + } + metadata.Host = "" // clear host to avoid double resolve in proxy + } + + conn, err := proxyAdapter.DialContext(ctx, metadata, opts...) + if err != nil { + return nil, err + } + + conn = statistic.NewTCPTracker(conn, statistic.DefaultManager, metadata, rule, 0, 0, false) + + return conn, nil } else { if proxyAdapter == nil { return dialer.DialContext(ctx, network, addr, opts...) @@ -258,6 +268,8 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, return nil, err } + packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false) + return N.NewBindPacketConn(packetConn, metadata.UDPAddr()), nil } } @@ -281,9 +293,10 @@ func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName st metadata.DstIP = dstIP } + var rule C.Rule if proxyAdapter == nil { if proxyName == RespectRules { - proxyAdapter, _, err = tunnel.ResolveMetadata(metadata) + proxyAdapter, rule, err = tunnel.ResolveMetadata(metadata) if err != nil { return nil, err } @@ -304,7 +317,14 @@ func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName st return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter) } - return proxyAdapter.ListenPacketContext(ctx, metadata, opts...) + packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...) + if err != nil { + return nil, err + } + + packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false) + + return packetConn, nil } func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, cache bool, err error) { From a5f25a22463110b87da06c46e777a029bad7894f Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 12 Jun 2024 20:54:12 +0800 Subject: [PATCH 24/38] chore: code split --- dns/dial.go | 169 ++++++++++++++++++++++++++++++++++++++++++++++++++++ dns/util.go | 157 ------------------------------------------------ 2 files changed, 169 insertions(+), 157 deletions(-) create mode 100644 dns/dial.go diff --git a/dns/dial.go b/dns/dial.go new file mode 100644 index 00000000..431707c5 --- /dev/null +++ b/dns/dial.go @@ -0,0 +1,169 @@ +package dns + +import ( + "context" + "fmt" + "net" + "net/netip" + "strings" + + N "github.com/metacubex/mihomo/common/net" + "github.com/metacubex/mihomo/component/dialer" + "github.com/metacubex/mihomo/component/resolver" + C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/tunnel" + "github.com/metacubex/mihomo/tunnel/statistic" +) + +const RespectRules = "RULES" + +type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error) + +func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) dialHandler { + return func(ctx context.Context, network, addr string) (net.Conn, error) { + if len(proxyName) == 0 && proxyAdapter == nil { + opts = append(opts, dialer.WithResolver(r)) + return dialer.DialContext(ctx, network, addr, opts...) + } else { + metadata := &C.Metadata{ + NetWork: C.TCP, + Type: C.INNER, + } + err := metadata.SetRemoteAddress(addr) // tcp can resolve host by remote + if err != nil { + return nil, err + } + if !strings.Contains(network, "tcp") { + metadata.NetWork = C.UDP + if !metadata.Resolved() { + // udp must resolve host first + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) + if err != nil { + return nil, err + } + metadata.DstIP = dstIP + } + } + + var rule C.Rule + if proxyAdapter == nil { + if proxyName == RespectRules { + if !metadata.Resolved() { + // resolve here before ResolveMetadata to avoid its inner resolver.ResolveIP + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) + if err != nil { + return nil, err + } + metadata.DstIP = dstIP + } + proxyAdapter, rule, err = tunnel.ResolveMetadata(metadata) + if err != nil { + return nil, err + } + } else { + var ok bool + proxyAdapter, ok = tunnel.Proxies()[proxyName] + if !ok { + opts = append(opts, dialer.WithInterface(proxyName)) + } + } + } + + if strings.Contains(network, "tcp") { + if proxyAdapter == nil { + opts = append(opts, dialer.WithResolver(r)) + return dialer.DialContext(ctx, network, addr, opts...) + } + + if proxyAdapter.IsL3Protocol(metadata) { // L3 proxy should resolve domain before to avoid loopback + if !metadata.Resolved() { + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) + if err != nil { + return nil, err + } + metadata.DstIP = dstIP + } + metadata.Host = "" // clear host to avoid double resolve in proxy + } + + conn, err := proxyAdapter.DialContext(ctx, metadata, opts...) + if err != nil { + return nil, err + } + + conn = statistic.NewTCPTracker(conn, statistic.DefaultManager, metadata, rule, 0, 0, false) + + return conn, nil + } else { + if proxyAdapter == nil { + return dialer.DialContext(ctx, network, addr, opts...) + } + + if !proxyAdapter.SupportUDP() { + return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter) + } + + packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...) + if err != nil { + return nil, err + } + + packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false) + + return N.NewBindPacketConn(packetConn, metadata.UDPAddr()), nil + } + } + } +} + +func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.PacketConn, error) { + metadata := &C.Metadata{ + NetWork: C.UDP, + Type: C.INNER, + } + err := metadata.SetRemoteAddress(addr) + if err != nil { + return nil, err + } + if !metadata.Resolved() { + // udp must resolve host first + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) + if err != nil { + return nil, err + } + metadata.DstIP = dstIP + } + + var rule C.Rule + if proxyAdapter == nil { + if proxyName == RespectRules { + proxyAdapter, rule, err = tunnel.ResolveMetadata(metadata) + if err != nil { + return nil, err + } + } else { + var ok bool + proxyAdapter, ok = tunnel.Proxies()[proxyName] + if !ok { + opts = append(opts, dialer.WithInterface(proxyName)) + } + } + } + + if proxyAdapter == nil { + return dialer.NewDialer(opts...).ListenPacket(ctx, network, "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort)) + } + + if !proxyAdapter.SupportUDP() { + return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter) + } + + packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...) + if err != nil { + return nil, err + } + + packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false) + + return packetConn, nil +} diff --git a/dns/util.go b/dns/util.go index f13279fa..9c5a0b58 100644 --- a/dns/util.go +++ b/dns/util.go @@ -10,15 +10,10 @@ import ( "strings" "time" - N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/common/nnip" "github.com/metacubex/mihomo/common/picker" - "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/resolver" - C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" - "github.com/metacubex/mihomo/tunnel" - "github.com/metacubex/mihomo/tunnel/statistic" D "github.com/miekg/dns" "github.com/samber/lo" @@ -175,158 +170,6 @@ func msgToDomain(msg *D.Msg) string { return "" } -const RespectRules = "RULES" - -type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error) - -func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) dialHandler { - return func(ctx context.Context, network, addr string) (net.Conn, error) { - if len(proxyName) == 0 && proxyAdapter == nil { - opts = append(opts, dialer.WithResolver(r)) - return dialer.DialContext(ctx, network, addr, opts...) - } else { - metadata := &C.Metadata{ - NetWork: C.TCP, - Type: C.INNER, - } - err := metadata.SetRemoteAddress(addr) // tcp can resolve host by remote - if err != nil { - return nil, err - } - if !strings.Contains(network, "tcp") { - metadata.NetWork = C.UDP - if !metadata.Resolved() { - // udp must resolve host first - dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) - if err != nil { - return nil, err - } - metadata.DstIP = dstIP - } - } - - var rule C.Rule - if proxyAdapter == nil { - if proxyName == RespectRules { - if !metadata.Resolved() { - // resolve here before ResolveMetadata to avoid its inner resolver.ResolveIP - dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) - if err != nil { - return nil, err - } - metadata.DstIP = dstIP - } - proxyAdapter, rule, err = tunnel.ResolveMetadata(metadata) - if err != nil { - return nil, err - } - } else { - var ok bool - proxyAdapter, ok = tunnel.Proxies()[proxyName] - if !ok { - opts = append(opts, dialer.WithInterface(proxyName)) - } - } - } - - if strings.Contains(network, "tcp") { - if proxyAdapter == nil { - opts = append(opts, dialer.WithResolver(r)) - return dialer.DialContext(ctx, network, addr, opts...) - } - - if proxyAdapter.IsL3Protocol(metadata) { // L3 proxy should resolve domain before to avoid loopback - if !metadata.Resolved() { - dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) - if err != nil { - return nil, err - } - metadata.DstIP = dstIP - } - metadata.Host = "" // clear host to avoid double resolve in proxy - } - - conn, err := proxyAdapter.DialContext(ctx, metadata, opts...) - if err != nil { - return nil, err - } - - conn = statistic.NewTCPTracker(conn, statistic.DefaultManager, metadata, rule, 0, 0, false) - - return conn, nil - } else { - if proxyAdapter == nil { - return dialer.DialContext(ctx, network, addr, opts...) - } - - if !proxyAdapter.SupportUDP() { - return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter) - } - - packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...) - if err != nil { - return nil, err - } - - packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false) - - return N.NewBindPacketConn(packetConn, metadata.UDPAddr()), nil - } - } - } -} - -func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.PacketConn, error) { - metadata := &C.Metadata{ - NetWork: C.UDP, - } - err := metadata.SetRemoteAddress(addr) - if err != nil { - return nil, err - } - if !metadata.Resolved() { - // udp must resolve host first - dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) - if err != nil { - return nil, err - } - metadata.DstIP = dstIP - } - - var rule C.Rule - if proxyAdapter == nil { - if proxyName == RespectRules { - proxyAdapter, rule, err = tunnel.ResolveMetadata(metadata) - if err != nil { - return nil, err - } - } else { - var ok bool - proxyAdapter, ok = tunnel.Proxies()[proxyName] - if !ok { - opts = append(opts, dialer.WithInterface(proxyName)) - } - } - } - - if proxyAdapter == nil { - return dialer.NewDialer(opts...).ListenPacket(ctx, network, "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort)) - } - - if !proxyAdapter.SupportUDP() { - return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter) - } - - packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...) - if err != nil { - return nil, err - } - - packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false) - - return packetConn, nil -} - func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, cache bool, err error) { cache = true fast, ctx := picker.WithTimeout[*D.Msg](ctx, resolver.DefaultDNSTimeout) From d96d7651ca7903e51a53033bfa7c18e6e7b15f50 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 13 Jun 2024 08:43:03 +0800 Subject: [PATCH 25/38] chore: add inner dns proxied connection log --- dns/dialer.go | 12 ++++ dns/dial.go => tunnel/dns_dialer.go | 33 +++++---- tunnel/tunnel.go | 103 ++++++++++------------------ 3 files changed, 70 insertions(+), 78 deletions(-) create mode 100644 dns/dialer.go rename dns/dial.go => tunnel/dns_dialer.go (77%) diff --git a/dns/dialer.go b/dns/dialer.go new file mode 100644 index 00000000..309b6c18 --- /dev/null +++ b/dns/dialer.go @@ -0,0 +1,12 @@ +package dns + +// export functions from tunnel module + +import "github.com/metacubex/mihomo/tunnel" + +const RespectRules = tunnel.DnsRespectRules + +type dialHandler = tunnel.DnsDialHandler + +var getDialHandler = tunnel.GetDnsDialHandler +var listenPacket = tunnel.DnsListenPacket diff --git a/dns/dial.go b/tunnel/dns_dialer.go similarity index 77% rename from dns/dial.go rename to tunnel/dns_dialer.go index 431707c5..f4c0be71 100644 --- a/dns/dial.go +++ b/tunnel/dns_dialer.go @@ -1,4 +1,6 @@ -package dns +package tunnel + +// WARNING: all function in this file should only be using in dns module import ( "context" @@ -11,15 +13,14 @@ import ( "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/resolver" C "github.com/metacubex/mihomo/constant" - "github.com/metacubex/mihomo/tunnel" "github.com/metacubex/mihomo/tunnel/statistic" ) -const RespectRules = "RULES" +const DnsRespectRules = "RULES" -type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error) +type DnsDialHandler func(ctx context.Context, network, addr string) (net.Conn, error) -func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) dialHandler { +func GetDnsDialHandler(r resolver.Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) DnsDialHandler { return func(ctx context.Context, network, addr string) (net.Conn, error) { if len(proxyName) == 0 && proxyAdapter == nil { opts = append(opts, dialer.WithResolver(r)) @@ -47,22 +48,22 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, var rule C.Rule if proxyAdapter == nil { - if proxyName == RespectRules { + if proxyName == DnsRespectRules { if !metadata.Resolved() { - // resolve here before ResolveMetadata to avoid its inner resolver.ResolveIP + // resolve here before resolveMetadata to avoid its inner resolver.ResolveIP dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) if err != nil { return nil, err } metadata.DstIP = dstIP } - proxyAdapter, rule, err = tunnel.ResolveMetadata(metadata) + proxyAdapter, rule, err = resolveMetadata(metadata) if err != nil { return nil, err } } else { var ok bool - proxyAdapter, ok = tunnel.Proxies()[proxyName] + proxyAdapter, ok = Proxies()[proxyName] if !ok { opts = append(opts, dialer.WithInterface(proxyName)) } @@ -88,8 +89,10 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, conn, err := proxyAdapter.DialContext(ctx, metadata, opts...) if err != nil { + logMetadataErr(metadata, rule, proxyAdapter, err) return nil, err } + logMetadata(metadata, rule, conn) conn = statistic.NewTCPTracker(conn, statistic.DefaultManager, metadata, rule, 0, 0, false) @@ -105,8 +108,10 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...) if err != nil { + logMetadataErr(metadata, rule, proxyAdapter, err) return nil, err } + logMetadata(metadata, rule, packetConn) packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false) @@ -116,7 +121,7 @@ func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, } } -func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.PacketConn, error) { +func DnsListenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r resolver.Resolver, opts ...dialer.Option) (net.PacketConn, error) { metadata := &C.Metadata{ NetWork: C.UDP, Type: C.INNER, @@ -136,14 +141,14 @@ func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName st var rule C.Rule if proxyAdapter == nil { - if proxyName == RespectRules { - proxyAdapter, rule, err = tunnel.ResolveMetadata(metadata) + if proxyName == DnsRespectRules { + proxyAdapter, rule, err = resolveMetadata(metadata) if err != nil { return nil, err } } else { var ok bool - proxyAdapter, ok = tunnel.Proxies()[proxyName] + proxyAdapter, ok = Proxies()[proxyName] if !ok { opts = append(opts, dialer.WithInterface(proxyName)) } @@ -160,8 +165,10 @@ func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName st packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...) if err != nil { + logMetadataErr(metadata, rule, proxyAdapter, err) return nil, err } + logMetadata(metadata, rule, packetConn) packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 78628a56..2c1b894f 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -8,6 +8,7 @@ import ( "net/netip" "path/filepath" "runtime" + "strings" "sync" "time" @@ -278,7 +279,7 @@ func preHandleMetadata(metadata *C.Metadata) error { return nil } -func ResolveMetadata(metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err error) { +func resolveMetadata(metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err error) { if metadata.SpecialProxy != "" { var exist bool proxy, exist = proxies[metadata.SpecialProxy] @@ -375,7 +376,7 @@ func handleUDPConn(packet C.PacketAdapter) { cond.Broadcast() }() - proxy, rule, err := ResolveMetadata(metadata) + proxy, rule, err := resolveMetadata(metadata) if err != nil { log.Warnln("[UDP] Parse metadata failed: %s", err.Error()) return @@ -386,43 +387,18 @@ func handleUDPConn(packet C.PacketAdapter) { rawPc, err := retry(ctx, func(ctx context.Context) (C.PacketConn, error) { return proxy.ListenPacketContext(ctx, metadata.Pure()) }, func(err error) { - if rule == nil { - log.Warnln( - "[UDP] dial %s %s --> %s error: %s", - proxy.Name(), - metadata.SourceDetail(), - metadata.RemoteAddress(), - err.Error(), - ) - } else { - log.Warnln("[UDP] dial %s (match %s/%s) %s --> %s error: %s", proxy.Name(), rule.RuleType().String(), rule.Payload(), metadata.SourceDetail(), metadata.RemoteAddress(), err.Error()) - } + logMetadataErr(metadata, rule, proxy, err) }) if err != nil { return } + logMetadata(metadata, rule, rawPc) pc := statistic.NewUDPTracker(rawPc, statistic.DefaultManager, metadata, rule, 0, 0, true) - switch true { - case metadata.SpecialProxy != "": - log.Infoln("[UDP] %s --> %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), metadata.SpecialProxy) - case rule != nil: - if rule.Payload() != "" { - log.Infoln("[UDP] %s --> %s match %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), fmt.Sprintf("%s(%s)", rule.RuleType().String(), rule.Payload()), rawPc.Chains().String()) - if rawPc.Chains().Last() == "REJECT-DROP" { - pc.Close() - return - } - } else { - log.Infoln("[UDP] %s --> %s match %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), rule.Payload(), rawPc.Chains().String()) - } - case mode == Global: - log.Infoln("[UDP] %s --> %s using GLOBAL", metadata.SourceDetail(), metadata.RemoteAddress()) - case mode == Direct: - log.Infoln("[UDP] %s --> %s using DIRECT", metadata.SourceDetail(), metadata.RemoteAddress()) - default: - log.Infoln("[UDP] %s --> %s doesn't match any rule using DIRECT", metadata.SourceDetail(), metadata.RemoteAddress()) + if rawPc.Chains().Last() == "REJECT-DROP" { + pc.Close() + return } oAddrPort := metadata.AddrPort() @@ -486,7 +462,7 @@ func handleTCPConn(connCtx C.ConnContext) { }() } - proxy, rule, err := ResolveMetadata(metadata) + proxy, rule, err := resolveMetadata(metadata) if err != nil { log.Warnln("[Metadata] parse failed: %s", err.Error()) return @@ -539,48 +515,18 @@ func handleTCPConn(connCtx C.ConnContext) { } return }, func(err error) { - if rule == nil { - log.Warnln( - "[TCP] dial %s %s --> %s error: %s", - proxy.Name(), - metadata.SourceDetail(), - metadata.RemoteAddress(), - err.Error(), - ) - } else { - log.Warnln("[TCP] dial %s (match %s/%s) %s --> %s error: %s", proxy.Name(), rule.RuleType().String(), rule.Payload(), metadata.SourceDetail(), metadata.RemoteAddress(), err.Error()) - } + logMetadataErr(metadata, rule, proxy, err) }) if err != nil { return } + logMetadata(metadata, rule, remoteConn) remoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule, 0, int64(peekLen), true) defer func(remoteConn C.Conn) { _ = remoteConn.Close() }(remoteConn) - switch true { - case metadata.SpecialProxy != "": - log.Infoln("[TCP] %s --> %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), metadata.SpecialProxy) - case rule != nil: - if rule.Payload() != "" { - log.Infoln("[TCP] %s --> %s match %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), fmt.Sprintf("%s(%s)", rule.RuleType().String(), rule.Payload()), remoteConn.Chains().String()) - } else { - log.Infoln("[TCP] %s --> %s match %s using %s", metadata.SourceDetail(), metadata.RemoteAddress(), rule.RuleType().String(), remoteConn.Chains().String()) - } - case mode == Global: - log.Infoln("[TCP] %s --> %s using GLOBAL", metadata.SourceDetail(), metadata.RemoteAddress()) - case mode == Direct: - log.Infoln("[TCP] %s --> %s using DIRECT", metadata.SourceDetail(), metadata.RemoteAddress()) - default: - log.Infoln( - "[TCP] %s --> %s doesn't match any rule using DIRECT", - metadata.SourceDetail(), - metadata.RemoteAddress(), - ) - } - _ = conn.SetReadDeadline(time.Now()) // stop unfinished peek peekMutex.Lock() defer peekMutex.Unlock() @@ -588,6 +534,33 @@ func handleTCPConn(connCtx C.ConnContext) { handleSocket(conn, remoteConn) } +func logMetadataErr(metadata *C.Metadata, rule C.Rule, proxy C.ProxyAdapter, err error) { + if rule == nil { + log.Warnln("[%s] dial %s %s --> %s error: %s", strings.ToUpper(metadata.NetWork.String()), proxy.Name(), metadata.SourceDetail(), metadata.RemoteAddress(), err.Error()) + } else { + log.Warnln("[%s] dial %s (match %s/%s) %s --> %s error: %s", strings.ToUpper(metadata.NetWork.String()), proxy.Name(), rule.RuleType().String(), rule.Payload(), metadata.SourceDetail(), metadata.RemoteAddress(), err.Error()) + } +} + +func logMetadata(metadata *C.Metadata, rule C.Rule, remoteConn C.Connection) { + switch { + case metadata.SpecialProxy != "": + log.Infoln("[%s] %s --> %s using %s", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress(), metadata.SpecialProxy) + case rule != nil: + if rule.Payload() != "" { + log.Infoln("[%s] %s --> %s match %s using %s", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress(), fmt.Sprintf("%s(%s)", rule.RuleType().String(), rule.Payload()), remoteConn.Chains().String()) + } else { + log.Infoln("[%s] %s --> %s match %s using %s", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress(), rule.RuleType().String(), remoteConn.Chains().String()) + } + case mode == Global: + log.Infoln("[%s] %s --> %s using GLOBAL", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress()) + case mode == Direct: + log.Infoln("[%s] %s --> %s using DIRECT", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress()) + default: + log.Infoln("[%s] %s --> %s doesn't match any rule using %s", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress(), remoteConn.Chains().Last()) + } +} + func shouldResolveIP(rule C.Rule, metadata *C.Metadata) bool { return rule.ShouldResolveIP() && metadata.Host != "" && !metadata.DstIP.IsValid() } From 75c16f9b87aa7fa5522592181f025b0cd7b61f36 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 14 Jun 2024 14:01:52 +0800 Subject: [PATCH 26/38] feat: add `refresh-server-ip-interval` for wireguard outbound --- adapter/outbound/wireguard.go | 157 +++++++++++++++++++++++++--------- docs/config.yaml | 1 + 2 files changed, 116 insertions(+), 42 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 56ade71c..2e34dd83 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -12,6 +12,7 @@ import ( "strconv" "strings" "sync" + "time" "github.com/metacubex/mihomo/common/atomic" CN "github.com/metacubex/mihomo/common/net" @@ -48,6 +49,10 @@ type WireGuard struct { connectAddr M.Socksaddr localPrefixes []netip.Prefix + serverAddrMap map[M.Socksaddr]netip.AddrPort + serverAddrTime atomic.TypedValue[time.Time] + serverAddrMutex sync.Mutex + closeCh chan struct{} // for test } @@ -67,6 +72,8 @@ type WireGuardOption struct { RemoteDnsResolve bool `proxy:"remote-dns-resolve,omitempty"` Dns []string `proxy:"dns,omitempty"` + + RefreshServerIPInterval int `proxy:"refresh-server-ip-interval,omitempty"` } type WireGuardPeerOption struct { @@ -287,6 +294,15 @@ func (w *WireGuard) resolve(ctx context.Context, address M.Socksaddr) (netip.Add } func (w *WireGuard) init(ctx context.Context) error { + err := w.init0(ctx) + if err != nil { + return err + } + w.updateServerAddr(ctx) + return nil +} + +func (w *WireGuard) init0(ctx context.Context) error { if w.initOk.Load() { return nil } @@ -301,44 +317,118 @@ func (w *WireGuard) init(ctx context.Context) error { } w.bind.ResetReservedForEndpoint() - ipcConf := "private_key=" + w.option.PrivateKey + w.serverAddrMap = make(map[M.Socksaddr]netip.AddrPort) + ipcConf, err := w.genIpcConf(ctx, false) + if err != nil { + // !!! do not set initErr here !!! + // let us can retry domain resolve in next time + return err + } + + if debug.Enabled { + log.SingLogger.Trace(fmt.Sprintf("[WG](%s) created wireguard ipc conf: \n %s", w.option.Name, ipcConf)) + } + err = w.device.IpcSet(ipcConf) + if err != nil { + w.initErr = E.Cause(err, "setup wireguard") + return w.initErr + } + w.serverAddrTime.Store(time.Now()) + + err = w.tunDevice.Start() + if err != nil { + w.initErr = err + return w.initErr + } + + w.initOk.Store(true) + return nil +} + +func (w *WireGuard) updateServerAddr(ctx context.Context) { + if w.option.RefreshServerIPInterval != 0 && time.Since(w.serverAddrTime.Load()) > time.Second*time.Duration(w.option.RefreshServerIPInterval) { + if w.serverAddrMutex.TryLock() { + defer w.serverAddrMutex.Unlock() + ipcConf, err := w.genIpcConf(ctx, true) + if err != nil { + log.Warnln("[WG](%s)UpdateServerAddr failed to generate wireguard ipc conf: %s", w.option.Name, err) + return + } + err = w.device.IpcSet(ipcConf) + if err != nil { + log.Warnln("[WG](%s)UpdateServerAddr failed to update wireguard ipc conf: %s", w.option.Name, err) + return + } + w.serverAddrTime.Store(time.Now()) + } + } +} + +func (w *WireGuard) genIpcConf(ctx context.Context, updateOnly bool) (string, error) { + ipcConf := "" + if !updateOnly { + ipcConf += "private_key=" + w.option.PrivateKey + "\n" + } if len(w.option.Peers) > 0 { for i, peer := range w.option.Peers { - ipcConf += "\npublic_key=" + peer.PublicKey - destination, err := w.resolve(ctx, peer.Addr()) + peerAddr := peer.Addr() + destination, err := w.resolve(ctx, peerAddr) 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) + return "", E.Cause(err, "resolve endpoint domain for peer ", i) } + if w.serverAddrMap[peerAddr] != destination { + w.serverAddrMap[peerAddr] = destination + } else if updateOnly { + continue + } + if len(w.option.Peers) == 1 { // must call SetConnectAddr if isConnect == true w.bind.SetConnectAddr(destination) } - ipcConf += "\nendpoint=" + destination.String() - if peer.PreSharedKey != "" { - ipcConf += "\npreshared_key=" + peer.PreSharedKey - } - for _, allowedIP := range peer.AllowedIPs { - ipcConf += "\nallowed_ip=" + allowedIP + ipcConf += "public_key=" + peer.PublicKey + "\n" + if updateOnly { + ipcConf += "update_only=true\n" } + ipcConf += "endpoint=" + destination.String() + "\n" if len(peer.Reserved) > 0 { var reserved [3]uint8 copy(reserved[:], w.option.Reserved) w.bind.SetReservedForEndpoint(destination, reserved) } + if updateOnly { + continue + } + if peer.PreSharedKey != "" { + ipcConf += "preshared_key=" + peer.PreSharedKey + "\n" + } + for _, allowedIP := range peer.AllowedIPs { + ipcConf += "allowed_ip=" + allowedIP + "\n" + } + if w.option.PersistentKeepalive != 0 { + ipcConf += fmt.Sprintf("persistent_keepalive_interval=%d\n", w.option.PersistentKeepalive) + } } } else { - ipcConf += "\npublic_key=" + w.option.PublicKey destination, err := w.resolve(ctx, w.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") + return "", E.Cause(err, "resolve endpoint domain") + } + if w.serverAddrMap[w.connectAddr] != destination { + w.serverAddrMap[w.connectAddr] = destination + } else if updateOnly { + return "", nil } w.bind.SetConnectAddr(destination) // must call SetConnectAddr if isConnect == true - ipcConf += "\nendpoint=" + destination.String() + ipcConf += "public_key=" + w.option.PublicKey + "\n" + if updateOnly { + ipcConf += "update_only=true\n" + } + ipcConf += "endpoint=" + destination.String() + "\n" + if updateOnly { + return ipcConf, nil + } if w.option.PreSharedKey != "" { - ipcConf += "\npreshared_key=" + w.option.PreSharedKey + ipcConf += "preshared_key=" + w.option.PreSharedKey + "\n" } var has4, has6 bool for _, address := range w.localPrefixes { @@ -349,34 +439,17 @@ func (w *WireGuard) init(ctx context.Context) error { } } if has4 { - ipcConf += "\nallowed_ip=0.0.0.0/0" + ipcConf += "allowed_ip=0.0.0.0/0\n" } if has6 { - ipcConf += "\nallowed_ip=::/0" + ipcConf += "allowed_ip=::/0\n" + } + + if w.option.PersistentKeepalive != 0 { + ipcConf += fmt.Sprintf("persistent_keepalive_interval=%d\n", w.option.PersistentKeepalive) } } - - if w.option.PersistentKeepalive != 0 { - ipcConf += fmt.Sprintf("\npersistent_keepalive_interval=%d", w.option.PersistentKeepalive) - } - - if debug.Enabled { - log.SingLogger.Trace(fmt.Sprintf("[WG](%s) created wireguard ipc conf: \n %s", w.option.Name, ipcConf)) - } - err := w.device.IpcSet(ipcConf) - if err != nil { - w.initErr = E.Cause(err, "setup wireguard") - return w.initErr - } - - err = w.tunDevice.Start() - if err != nil { - w.initErr = err - return w.initErr - } - - w.initOk.Store(true) - return nil + return ipcConf, nil } func closeWireGuard(w *WireGuard) { diff --git a/docs/config.yaml b/docs/config.yaml index bd263c14..462b3a44 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -728,6 +728,7 @@ proxies: # socks5 # dialer-proxy: "ss1" # remote-dns-resolve: true # 强制 dns 远程解析,默认值为 false # dns: [ 1.1.1.1, 8.8.8.8 ] # 仅在 remote-dns-resolve 为 true 时生效 + # refresh-server-ip-interval: 60 # 重新解析server ip的间隔,单位为秒,默认值为0即仅第一次链接时解析server域名,仅应在server域名对应的IP会发生变化时启用该选项(如家宽ddns) # 如果 peers 不为空,该段落中的 allowed-ips 不可为空;前面段落的 server,port,public-key,pre-shared-key 均会被忽略,但 private-key 会被保留且只能在顶层指定 # peers: # - server: 162.159.192.1 From ad5bc51c77088e0abb04dfbc8dbb5e7d81d6afbf Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 15 Jun 2024 12:14:46 +0800 Subject: [PATCH 27/38] chore: deprecated the relay group type, please using dialer-proxy instead --- adapter/outboundgroup/relay.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/adapter/outboundgroup/relay.go b/adapter/outboundgroup/relay.go index 07fbcd95..29aa9c6a 100644 --- a/adapter/outboundgroup/relay.go +++ b/adapter/outboundgroup/relay.go @@ -9,6 +9,7 @@ import ( "github.com/metacubex/mihomo/component/proxydialer" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/constant/provider" + "github.com/metacubex/mihomo/log" ) type Relay struct { @@ -149,6 +150,7 @@ func (r *Relay) Addr() string { } func NewRelay(option *GroupCommonOption, providers []provider.ProxyProvider) *Relay { + log.Warnln("The group [%s] with relay type is deprecated, please using dialer-proxy instead", option.Name) return &Relay{ GroupBase: NewGroupBase(GroupBaseOption{ outbound.BaseOption{ From 40f40f6d2423c093add49820894dd6d22346a9f4 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 15 Jun 2024 00:33:03 +0800 Subject: [PATCH 28/38] fix: dns dial to wrong target --- adapter/outbound/direct.go | 3 +- dns/client.go | 47 ++------- dns/dialer.go | 5 +- dns/doh.go | 35 +++---- dns/doq.go | 16 ++- dns/util.go | 15 +-- tunnel/dns_dialer.go | 196 +++++++++++++++++++------------------ 7 files changed, 142 insertions(+), 175 deletions(-) diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go index 09b9696b..c904d3b6 100644 --- a/adapter/outbound/direct.go +++ b/adapter/outbound/direct.go @@ -3,7 +3,6 @@ package outbound import ( "context" "errors" - "net/netip" "os" "strconv" @@ -58,7 +57,7 @@ func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, } metadata.DstIP = ip } - pc, err := dialer.NewDialer(d.Base.DialOptions(opts...)...).ListenPacket(ctx, "udp", "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort)) + pc, err := dialer.NewDialer(d.Base.DialOptions(opts...)...).ListenPacket(ctx, "udp", "", metadata.AddrPort()) if err != nil { return nil, err } diff --git a/dns/client.go b/dns/client.go index a6f0a7d4..096b96a7 100644 --- a/dns/client.go +++ b/dns/client.go @@ -5,28 +5,20 @@ import ( "crypto/tls" "fmt" "net" - "net/netip" "strings" "github.com/metacubex/mihomo/component/ca" - "github.com/metacubex/mihomo/component/dialer" - "github.com/metacubex/mihomo/component/resolver" - C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" - "github.com/metacubex/randv2" D "github.com/miekg/dns" ) type client struct { *D.Client - r *Resolver - port string - host string - iface string - proxyAdapter C.ProxyAdapter - proxyName string - addr string + port string + host string + dialer *dnsDialer + addr string } var _ dnsClient = (*client)(nil) @@ -49,38 +41,13 @@ func (c *client) Address() string { } func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) { - var ( - ip netip.Addr - err error - ) - if c.r == nil { - // a default ip dns - if ip, err = netip.ParseAddr(c.host); err != nil { - return nil, fmt.Errorf("dns %s not a valid ip", c.host) - } - } else { - ips, err := resolver.LookupIPWithResolver(ctx, c.host, c.r) - if err != nil { - return nil, fmt.Errorf("use default dns resolve failed: %w", err) - } else if len(ips) == 0 { - return nil, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, c.host) - } - ip = ips[randv2.IntN(len(ips))] - } - network := "udp" if strings.HasPrefix(c.Client.Net, "tcp") { network = "tcp" } - var options []dialer.Option - if c.iface != "" { - options = append(options, dialer.WithInterface(c.iface)) - } - - dialHandler := getDialHandler(c.r, c.proxyAdapter, c.proxyName, options...) - addr := net.JoinHostPort(ip.String(), c.port) - conn, err := dialHandler(ctx, network, addr) + addr := net.JoinHostPort(c.host, c.port) + conn, err := c.dialer.DialContext(ctx, network, addr) if err != nil { return nil, err } @@ -115,7 +82,7 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) 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) + dConn.Conn, err = c.dialer.DialContext(ctx, network, addr) if err != nil { ch <- result{msg, err} return diff --git a/dns/dialer.go b/dns/dialer.go index 309b6c18..f4d9e128 100644 --- a/dns/dialer.go +++ b/dns/dialer.go @@ -6,7 +6,6 @@ import "github.com/metacubex/mihomo/tunnel" const RespectRules = tunnel.DnsRespectRules -type dialHandler = tunnel.DnsDialHandler +type dnsDialer = tunnel.DNSDialer -var getDialHandler = tunnel.GetDnsDialHandler -var listenPacket = tunnel.DnsListenPacket +var newDNSDialer = tunnel.NewDNSDialer diff --git a/dns/doh.go b/dns/doh.go index 09d311b5..54b82796 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -62,10 +62,8 @@ type dnsOverHTTPS struct { quicConfig *quic.Config quicConfigGuard sync.Mutex url *url.URL - r *Resolver httpVersions []C.HTTPVersion - proxyAdapter C.ProxyAdapter - proxyName string + dialer *dnsDialer addr string } @@ -85,11 +83,9 @@ func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[strin } doh := &dnsOverHTTPS{ - url: u, - addr: u.String(), - r: r, - proxyAdapter: proxyAdapter, - proxyName: proxyName, + url: u, + addr: u.String(), + dialer: newDNSDialer(r, proxyAdapter, proxyName), quicConfig: &quic.Config{ KeepAlivePeriod: QUICKeepAlivePeriod, TokenStore: newQUICTokenStore(), @@ -388,13 +384,12 @@ func (doh *dnsOverHTTPS) createTransport(ctx context.Context) (t http.RoundTripp nextProtos = append(nextProtos, string(v)) } tlsConfig.NextProtos = nextProtos - dialContext := getDialHandler(doh.r, doh.proxyAdapter, doh.proxyName) if slices.Contains(doh.httpVersions, C.HTTPVersion3) { // First, we attempt to create an HTTP3 transport. If the probe QUIC // connection is established successfully, we'll be using HTTP3 for this // upstream. - transportH3, err := doh.createTransportH3(ctx, tlsConfig, dialContext) + transportH3, err := doh.createTransportH3(ctx, tlsConfig) if err == nil { log.Debugln("[%s] using HTTP/3 for this upstream: QUIC was faster", doh.url.String()) return transportH3, nil @@ -410,7 +405,7 @@ func (doh *dnsOverHTTPS) createTransport(ctx context.Context) (t http.RoundTripp transport := &http.Transport{ TLSClientConfig: tlsConfig, DisableCompression: true, - DialContext: dialContext, + DialContext: doh.dialer.DialContext, IdleConnTimeout: transportDefaultIdleConnTimeout, MaxConnsPerHost: dohMaxConnsPerHost, MaxIdleConns: dohMaxIdleConns, @@ -490,13 +485,12 @@ func (h *http3Transport) Close() (err error) { func (doh *dnsOverHTTPS) createTransportH3( ctx context.Context, tlsConfig *tls.Config, - dialContext dialHandler, ) (roundTripper http.RoundTripper, err error) { if !doh.supportsH3() { return nil, errors.New("HTTP3 support is not enabled") } - addr, err := doh.probeH3(ctx, tlsConfig, dialContext) + addr, err := doh.probeH3(ctx, tlsConfig) if err != nil { return nil, err } @@ -534,7 +528,7 @@ func (doh *dnsOverHTTPS) dialQuic(ctx context.Context, addr string, tlsCfg *tls. IP: net.ParseIP(ip), Port: portInt, } - conn, err := listenPacket(ctx, doh.proxyAdapter, doh.proxyName, "udp", addr, doh.r) + conn, err := doh.dialer.ListenPacket(ctx, "udp", addr) if err != nil { return nil, err } @@ -557,12 +551,11 @@ func (doh *dnsOverHTTPS) dialQuic(ctx context.Context, addr string, tlsCfg *tls. func (doh *dnsOverHTTPS) probeH3( ctx context.Context, tlsConfig *tls.Config, - dialContext dialHandler, ) (addr string, err error) { // We're using bootstrapped address instead of what's passed to the function // it does not create an actual connection, but it helps us determine // what IP is actually reachable (when there are v4/v6 addresses). - rawConn, err := dialContext(ctx, "udp", doh.url.Host) + rawConn, err := doh.dialer.DialContext(ctx, "udp", doh.url.Host) if err != nil { return "", fmt.Errorf("failed to dial: %w", err) } @@ -592,7 +585,7 @@ func (doh *dnsOverHTTPS) probeH3( chQuic := make(chan error, 1) chTLS := make(chan error, 1) go doh.probeQUIC(ctx, addr, probeTLSCfg, chQuic) - go doh.probeTLS(ctx, dialContext, probeTLSCfg, chTLS) + go doh.probeTLS(ctx, probeTLSCfg, chTLS) select { case quicErr := <-chQuic: @@ -635,10 +628,10 @@ func (doh *dnsOverHTTPS) probeQUIC(ctx context.Context, addr string, tlsConfig * // probeTLS attempts to establish a TLS connection to the specified address. We // run probeQUIC and probeTLS in parallel and see which one is faster. -func (doh *dnsOverHTTPS) probeTLS(ctx context.Context, dialContext dialHandler, tlsConfig *tls.Config, ch chan error) { +func (doh *dnsOverHTTPS) probeTLS(ctx context.Context, tlsConfig *tls.Config, ch chan error) { startTime := time.Now() - conn, err := doh.tlsDial(ctx, dialContext, "tcp", tlsConfig) + conn, err := doh.tlsDial(ctx, "tcp", tlsConfig) if err != nil { ch <- fmt.Errorf("opening TLS connection: %w", err) return @@ -694,10 +687,10 @@ func isHTTP3(client *http.Client) (ok bool) { // tlsDial is basically the same as tls.DialWithDialer, but we will call our own // dialContext function to get connection. -func (doh *dnsOverHTTPS) tlsDial(ctx context.Context, dialContext dialHandler, network string, config *tls.Config) (*tls.Conn, error) { +func (doh *dnsOverHTTPS) tlsDial(ctx context.Context, network string, config *tls.Config) (*tls.Conn, error) { // We're using bootstrapped address instead of what's passed // to the function. - rawConn, err := dialContext(ctx, network, doh.url.Host) + rawConn, err := doh.dialer.DialContext(ctx, network, doh.url.Host) if err != nil { return nil, err } diff --git a/dns/doq.go b/dns/doq.go index 70b67c2a..ad936f95 100644 --- a/dns/doq.go +++ b/dns/doq.go @@ -60,10 +60,8 @@ type dnsOverQUIC struct { bytesPool *sync.Pool bytesPoolGuard sync.Mutex - addr string - proxyAdapter C.ProxyAdapter - proxyName string - r *Resolver + addr string + dialer *dnsDialer } // type check @@ -72,10 +70,8 @@ var _ dnsClient = (*dnsOverQUIC)(nil) // newDoQ returns the DNS-over-QUIC Upstream. func newDoQ(resolver *Resolver, addr string, proxyAdapter C.ProxyAdapter, proxyName string) (dnsClient, error) { doq := &dnsOverQUIC{ - addr: addr, - proxyAdapter: proxyAdapter, - proxyName: proxyName, - r: resolver, + addr: addr, + dialer: newDNSDialer(resolver, proxyAdapter, proxyName), quicConfig: &quic.Config{ KeepAlivePeriod: QUICKeepAlivePeriod, TokenStore: newQUICTokenStore(), @@ -300,7 +296,7 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connectio // we're using bootstrapped address instead of what's passed to the function // it does not create an actual connection, but it helps us determine // what IP is actually reachable (when there're v4/v6 addresses). - rawConn, err := getDialHandler(doq.r, doq.proxyAdapter, doq.proxyName)(ctx, "udp", doq.addr) + rawConn, err := doq.dialer.DialContext(ctx, "udp", doq.addr) if err != nil { return nil, fmt.Errorf("failed to open a QUIC connection: %w", err) } @@ -315,7 +311,7 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connectio p, err := strconv.Atoi(port) udpAddr := net.UDPAddr{IP: net.ParseIP(ip), Port: p} - udp, err := listenPacket(ctx, doq.proxyAdapter, doq.proxyName, "udp", addr, doq.r) + udp, err := doq.dialer.ListenPacket(ctx, "udp", addr) if err != nil { return nil, err } diff --git a/dns/util.go b/dns/util.go index 9c5a0b58..e4ec5917 100644 --- a/dns/util.go +++ b/dns/util.go @@ -12,6 +12,7 @@ import ( "github.com/metacubex/mihomo/common/nnip" "github.com/metacubex/mihomo/common/picker" + "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/resolver" "github.com/metacubex/mihomo/log" @@ -115,6 +116,11 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient { continue } + var options []dialer.Option + if s.Interface != "" { + options = append(options, dialer.WithInterface(s.Interface)) + } + host, port, _ := net.SplitHostPort(s.Addr) ret = append(ret, &client{ Client: &D.Client{ @@ -125,12 +131,9 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient { UDPSize: 4096, Timeout: 5 * time.Second, }, - port: port, - host: host, - iface: s.Interface, - r: resolver, - proxyAdapter: s.ProxyAdapter, - proxyName: s.ProxyName, + port: port, + host: host, + dialer: newDNSDialer(resolver, s.ProxyAdapter, s.ProxyName, options...), }) } return ret diff --git a/tunnel/dns_dialer.go b/tunnel/dns_dialer.go index f4c0be71..1839869b 100644 --- a/tunnel/dns_dialer.go +++ b/tunnel/dns_dialer.go @@ -6,7 +6,6 @@ import ( "context" "fmt" "net" - "net/netip" "strings" N "github.com/metacubex/mihomo/common/net" @@ -18,110 +17,121 @@ import ( const DnsRespectRules = "RULES" -type DnsDialHandler func(ctx context.Context, network, addr string) (net.Conn, error) +type DNSDialer struct { + r resolver.Resolver + proxyAdapter C.ProxyAdapter + proxyName string + opts []dialer.Option +} -func GetDnsDialHandler(r resolver.Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) DnsDialHandler { - return func(ctx context.Context, network, addr string) (net.Conn, error) { - if len(proxyName) == 0 && proxyAdapter == nil { - opts = append(opts, dialer.WithResolver(r)) - return dialer.DialContext(ctx, network, addr, opts...) - } else { - metadata := &C.Metadata{ - NetWork: C.TCP, - Type: C.INNER, - } - err := metadata.SetRemoteAddress(addr) // tcp can resolve host by remote +func NewDNSDialer(r resolver.Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) *DNSDialer { + return &DNSDialer{r: r, proxyAdapter: proxyAdapter, proxyName: proxyName, opts: opts} +} + +func (d *DNSDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) { + r := d.r + proxyName := d.proxyName + proxyAdapter := d.proxyAdapter + opts := d.opts + var rule C.Rule + metadata := &C.Metadata{ + NetWork: C.TCP, + Type: C.INNER, + } + err := metadata.SetRemoteAddress(addr) // tcp can resolve host by remote + if err != nil { + return nil, err + } + if !strings.Contains(network, "tcp") { + metadata.NetWork = C.UDP + if !metadata.Resolved() { + // udp must resolve host first + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) if err != nil { return nil, err } - if !strings.Contains(network, "tcp") { - metadata.NetWork = C.UDP - if !metadata.Resolved() { - // udp must resolve host first - dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) - if err != nil { - return nil, err - } - metadata.DstIP = dstIP - } - } + metadata.DstIP = dstIP + } + } - var rule C.Rule - if proxyAdapter == nil { - if proxyName == DnsRespectRules { - if !metadata.Resolved() { - // resolve here before resolveMetadata to avoid its inner resolver.ResolveIP - dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) - if err != nil { - return nil, err - } - metadata.DstIP = dstIP - } - proxyAdapter, rule, err = resolveMetadata(metadata) - if err != nil { - return nil, err - } - } else { - var ok bool - proxyAdapter, ok = Proxies()[proxyName] - if !ok { - opts = append(opts, dialer.WithInterface(proxyName)) - } - } - } - - if strings.Contains(network, "tcp") { - if proxyAdapter == nil { - opts = append(opts, dialer.WithResolver(r)) - return dialer.DialContext(ctx, network, addr, opts...) - } - - if proxyAdapter.IsL3Protocol(metadata) { // L3 proxy should resolve domain before to avoid loopback - if !metadata.Resolved() { - dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) - if err != nil { - return nil, err - } - metadata.DstIP = dstIP - } - metadata.Host = "" // clear host to avoid double resolve in proxy - } - - conn, err := proxyAdapter.DialContext(ctx, metadata, opts...) + if proxyAdapter == nil && len(proxyName) != 0 { + if proxyName == DnsRespectRules { + if !metadata.Resolved() { + // resolve here before resolveMetadata to avoid its inner resolver.ResolveIP + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) if err != nil { - logMetadataErr(metadata, rule, proxyAdapter, err) return nil, err } - logMetadata(metadata, rule, conn) - - conn = statistic.NewTCPTracker(conn, statistic.DefaultManager, metadata, rule, 0, 0, false) - - return conn, nil - } else { - if proxyAdapter == nil { - return dialer.DialContext(ctx, network, addr, opts...) - } - - if !proxyAdapter.SupportUDP() { - return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter) - } - - packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...) - if err != nil { - logMetadataErr(metadata, rule, proxyAdapter, err) - return nil, err - } - logMetadata(metadata, rule, packetConn) - - packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false) - - return N.NewBindPacketConn(packetConn, metadata.UDPAddr()), nil + metadata.DstIP = dstIP + } + proxyAdapter, rule, err = resolveMetadata(metadata) + if err != nil { + return nil, err + } + } else { + var ok bool + proxyAdapter, ok = Proxies()[proxyName] + if !ok { + opts = append(opts, dialer.WithInterface(proxyName)) } } } + + if metadata.NetWork == C.TCP { + if proxyAdapter == nil { + opts = append(opts, dialer.WithResolver(r)) + return dialer.DialContext(ctx, network, addr, opts...) + } + + if proxyAdapter.IsL3Protocol(metadata) { // L3 proxy should resolve domain before to avoid loopback + if !metadata.Resolved() { + dstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r) + if err != nil { + return nil, err + } + metadata.DstIP = dstIP + } + metadata.Host = "" // clear host to avoid double resolve in proxy + } + + conn, err := proxyAdapter.DialContext(ctx, metadata, opts...) + if err != nil { + logMetadataErr(metadata, rule, proxyAdapter, err) + return nil, err + } + logMetadata(metadata, rule, conn) + + conn = statistic.NewTCPTracker(conn, statistic.DefaultManager, metadata, rule, 0, 0, false) + + return conn, nil + } else { + if proxyAdapter == nil { + return dialer.DialContext(ctx, network, metadata.AddrPort().String(), opts...) + } + + if !proxyAdapter.SupportUDP() { + return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter) + } + + packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...) + if err != nil { + logMetadataErr(metadata, rule, proxyAdapter, err) + return nil, err + } + logMetadata(metadata, rule, packetConn) + + packetConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false) + + return N.NewBindPacketConn(packetConn, metadata.UDPAddr()), nil + } + } -func DnsListenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r resolver.Resolver, opts ...dialer.Option) (net.PacketConn, error) { +func (d *DNSDialer) ListenPacket(ctx context.Context, network, addr string) (net.PacketConn, error) { + r := d.r + proxyAdapter := d.proxyAdapter + proxyName := d.proxyName + opts := d.opts metadata := &C.Metadata{ NetWork: C.UDP, Type: C.INNER, @@ -156,7 +166,7 @@ func DnsListenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName } if proxyAdapter == nil { - return dialer.NewDialer(opts...).ListenPacket(ctx, network, "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort)) + return dialer.NewDialer(opts...).ListenPacket(ctx, network, "", metadata.AddrPort()) } if !proxyAdapter.SupportUDP() { From 0738e18100b625b0f3312f39e53c30a617514bbe Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sun, 16 Jun 2024 18:19:04 +0800 Subject: [PATCH 29/38] chore: add override fields --- adapter/provider/parser.go | 3 +++ adapter/provider/provider.go | 49 +++++++++++++----------------------- 2 files changed, 21 insertions(+), 31 deletions(-) diff --git a/adapter/provider/parser.go b/adapter/provider/parser.go index 1094668d..edb6b911 100644 --- a/adapter/provider/parser.go +++ b/adapter/provider/parser.go @@ -28,7 +28,10 @@ type healthCheckSchema struct { } type OverrideSchema struct { + TFO *bool `provider:"tfo,omitempty"` + MPTcp *bool `provider:"mptcp,omitempty"` UDP *bool `provider:"udp,omitempty"` + UDPOverTCP *bool `provider:"udp-over-tcp,omitempty"` Up *string `provider:"up,omitempty"` Down *string `provider:"down,omitempty"` DialerProxy *string `provider:"dialer-proxy,omitempty"` diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index daef017c..694eae43 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "net/http" + "reflect" "runtime" "strings" "time" @@ -373,37 +374,23 @@ func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray mapping["dialer-proxy"] = dialerProxy } - if override.UDP != nil { - mapping["udp"] = *override.UDP - } - if override.Up != nil { - mapping["up"] = *override.Up - } - if override.Down != nil { - mapping["down"] = *override.Down - } - if override.DialerProxy != nil { - mapping["dialer-proxy"] = *override.DialerProxy - } - if override.SkipCertVerify != nil { - mapping["skip-cert-verify"] = *override.SkipCertVerify - } - if override.Interface != nil { - mapping["interface-name"] = *override.Interface - } - if override.RoutingMark != nil { - mapping["routing-mark"] = *override.RoutingMark - } - 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 + val := reflect.ValueOf(override) + for i := 0; i < val.NumField(); i++ { + field := val.Field(i) + if field.IsNil() { + continue + } + fieldName := strings.Split(val.Type().Field(i).Tag.Get("provider"), ",")[0] + switch fieldName { + case "additional-prefix": + name := mapping["name"].(string) + mapping["name"] = *field.Interface().(*string) + name + case "additional-suffix": + name := mapping["name"].(string) + mapping["name"] = name + *field.Interface().(*string) + default: + mapping[fieldName] = field.Elem().Interface() + } } proxy, err := adapter.ParseProxy(mapping) From 09be5cbc99f97238aa95b9ceab9db39e53e1b3a9 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 17 Jun 2024 22:04:51 +0800 Subject: [PATCH 30/38] feat: tun support `auto-redirect`, `route-address-set` and `route-exclude-address-set` --- common/utils/callback.go | 50 +++++++ component/cidr/ipcidr_set.go | 8 +- component/iface/iface.go | 10 +- config/config.go | 107 +++++++++------ constant/provider/interface.go | 8 +- constant/rule.go | 1 + dns/policy.go | 13 +- dns/resolver.go | 9 +- docs/config.yaml | 16 ++- go.mod | 25 ++-- go.sum | 49 +++---- hub/executor/executor.go | 12 +- hub/route/configs.go | 72 +++++++--- listener/config/tun.go | 47 ++++--- listener/inbound/tun.go | 116 ++++++++++------ listener/listener.go | 28 +++- listener/sing/sing.go | 6 + listener/sing_tun/dns.go | 7 + listener/sing_tun/iface.go | 70 ++++++++++ listener/sing_tun/server.go | 211 +++++++++++++++++++++++++++-- listener/tproxy/tproxy_iptables.go | 4 +- rules/common/base.go | 2 + rules/logic/logic.go | 7 + rules/provider/ipcidr_strategy.go | 6 + rules/provider/patch_android.go | 4 +- rules/provider/provider.go | 34 ++--- rules/provider/rule_set.go | 51 ++++--- tunnel/tunnel.go | 19 ++- 28 files changed, 745 insertions(+), 247 deletions(-) create mode 100644 common/utils/callback.go create mode 100644 listener/sing_tun/iface.go diff --git a/common/utils/callback.go b/common/utils/callback.go new file mode 100644 index 00000000..df950d3a --- /dev/null +++ b/common/utils/callback.go @@ -0,0 +1,50 @@ +package utils + +import ( + "io" + "sync" + + list "github.com/bahlo/generic-list-go" +) + +type Callback[T any] struct { + list list.List[func(T)] + mutex sync.RWMutex +} + +func NewCallback[T any]() *Callback[T] { + return &Callback[T]{} +} + +func (c *Callback[T]) Register(item func(T)) io.Closer { + c.mutex.RLock() + defer c.mutex.RUnlock() + element := c.list.PushBack(item) + return &callbackCloser[T]{ + element: element, + callback: c, + } +} + +func (c *Callback[T]) Emit(item T) { + c.mutex.RLock() + defer c.mutex.RUnlock() + for element := c.list.Front(); element != nil; element = element.Next() { + go element.Value(item) + } +} + +type callbackCloser[T any] struct { + element *list.Element[func(T)] + callback *Callback[T] + once sync.Once +} + +func (c *callbackCloser[T]) Close() error { + c.once.Do(func() { + c.callback.mutex.Lock() + defer c.callback.mutex.Unlock() + c.callback.list.Remove(c.element) + }) + return nil +} diff --git a/component/cidr/ipcidr_set.go b/component/cidr/ipcidr_set.go index 0cb55e36..521fabab 100644 --- a/component/cidr/ipcidr_set.go +++ b/component/cidr/ipcidr_set.go @@ -43,12 +43,12 @@ func (set *IpCidrSet) IsContainForString(ipString string) bool { } func (set *IpCidrSet) IsContain(ip netip.Addr) bool { - return set.toIPSet().Contains(ip.WithZone("")) + return set.ToIPSet().Contains(ip.WithZone("")) } func (set *IpCidrSet) Merge() error { var b netipx.IPSetBuilder - b.AddSet(set.toIPSet()) + b.AddSet(set.ToIPSet()) i, err := b.IPSet() if err != nil { return err @@ -57,7 +57,9 @@ func (set *IpCidrSet) Merge() error { return nil } -func (set *IpCidrSet) toIPSet() *netipx.IPSet { +// ToIPSet not safe convert to *netipx.IPSet +// be careful, must be used after Merge +func (set *IpCidrSet) ToIPSet() *netipx.IPSet { return (*netipx.IPSet)(unsafe.Pointer(set)) } diff --git a/component/iface/iface.go b/component/iface/iface.go index d543725a..272ee737 100644 --- a/component/iface/iface.go +++ b/component/iface/iface.go @@ -11,8 +11,9 @@ import ( type Interface struct { Index int + MTU int Name string - Addrs []netip.Prefix + Addresses []netip.Prefix HardwareAddr net.HardwareAddr } @@ -61,8 +62,9 @@ func Interfaces() (map[string]*Interface, error) { r[iface.Name] = &Interface{ Index: iface.Index, + MTU: iface.MTU, Name: iface.Name, - Addrs: ipNets, + Addresses: ipNets, HardwareAddr: iface.HardwareAddr, } } @@ -92,7 +94,7 @@ func IsLocalIp(ip netip.Addr) (bool, error) { return false, err } for _, iface := range ifaces { - for _, addr := range iface.Addrs { + for _, addr := range iface.Addresses { if addr.Contains(ip) { return true, nil } @@ -120,7 +122,7 @@ func (iface *Interface) PickIPv6Addr(destination netip.Addr) (netip.Prefix, erro func (iface *Interface) pickIPAddr(destination netip.Addr, accept func(addr netip.Prefix) bool) (netip.Prefix, error) { var fallback netip.Prefix - for _, addr := range iface.Addrs { + for _, addr := range iface.Addresses { if !accept(addr) { continue } diff --git a/config/config.go b/config/config.go index fd12d2db..2166b87d 100644 --- a/config/config.go +++ b/config/config.go @@ -246,31 +246,39 @@ type RawTun struct { DNSHijack []string `yaml:"dns-hijack" json:"dns-hijack"` AutoRoute bool `yaml:"auto-route" json:"auto-route"` AutoDetectInterface bool `yaml:"auto-detect-interface"` - RedirectToTun []string `yaml:"-" json:"-"` MTU uint32 `yaml:"mtu" json:"mtu,omitempty"` GSO bool `yaml:"gso" json:"gso,omitempty"` GSOMaxSize uint32 `yaml:"gso-max-size" json:"gso-max-size,omitempty"` //Inet4Address []netip.Prefix `yaml:"inet4-address" json:"inet4_address,omitempty"` - Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6_address,omitempty"` - StrictRoute bool `yaml:"strict-route" json:"strict_route,omitempty"` + Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6_address,omitempty"` + IPRoute2TableIndex int `yaml:"iproute2-table-index" json:"iproute2_table_index,omitempty"` + IPRoute2RuleIndex int `yaml:"iproute2-rule-index" json:"iproute2_rule_index,omitempty"` + AutoRedirect bool `yaml:"auto-redirect" json:"auto_redirect,omitempty"` + AutoRedirectInputMark uint32 `yaml:"auto-redirect-input-mark" json:"auto_redirect_input_mark,omitempty"` + AutoRedirectOutputMark uint32 `yaml:"auto-redirect-output-mark" json:"auto_redirect_output_mark,omitempty"` + StrictRoute bool `yaml:"strict-route" json:"strict_route,omitempty"` + RouteAddress []netip.Prefix `yaml:"route-address" json:"route_address,omitempty"` + RouteAddressSet []string `yaml:"route-address-set" json:"route_address_set,omitempty"` + RouteExcludeAddress []netip.Prefix `yaml:"route-exclude-address" json:"route_exclude_address,omitempty"` + RouteExcludeAddressSet []string `yaml:"route-exclude-address-set" json:"route_exclude_address_set,omitempty"` + IncludeInterface []string `yaml:"include-interface" json:"include-interface,omitempty"` + ExcludeInterface []string `yaml:"exclude-interface" json:"exclude-interface,omitempty"` + IncludeUID []uint32 `yaml:"include-uid" json:"include_uid,omitempty"` + IncludeUIDRange []string `yaml:"include-uid-range" json:"include_uid_range,omitempty"` + ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude_uid,omitempty"` + ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude_uid_range,omitempty"` + IncludeAndroidUser []int `yaml:"include-android-user" json:"include_android_user,omitempty"` + IncludePackage []string `yaml:"include-package" json:"include_package,omitempty"` + ExcludePackage []string `yaml:"exclude-package" json:"exclude_package,omitempty"` + 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"` + Inet4RouteAddress []netip.Prefix `yaml:"inet4-route-address" json:"inet4_route_address,omitempty"` Inet6RouteAddress []netip.Prefix `yaml:"inet6-route-address" json:"inet6_route_address,omitempty"` Inet4RouteExcludeAddress []netip.Prefix `yaml:"inet4-route-exclude-address" json:"inet4_route_exclude_address,omitempty"` Inet6RouteExcludeAddress []netip.Prefix `yaml:"inet6-route-exclude-address" json:"inet6_route_exclude_address,omitempty"` - IncludeInterface []string `yaml:"include-interface" json:"include-interface,omitempty"` - ExcludeInterface []string `yaml:"exclude-interface" json:"exclude-interface,omitempty"` - IncludeUID []uint32 `yaml:"include-uid" json:"include_uid,omitempty"` - IncludeUIDRange []string `yaml:"include-uid-range" json:"include_uid_range,omitempty"` - ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude_uid,omitempty"` - ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude_uid_range,omitempty"` - IncludeAndroidUser []int `yaml:"include-android-user" json:"include_android_user,omitempty"` - IncludePackage []string `yaml:"include-package" json:"include_package,omitempty"` - ExcludePackage []string `yaml:"exclude-package" json:"exclude_package,omitempty"` - 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 { @@ -564,13 +572,13 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } config.RuleProviders = ruleProviders - subRules, err := parseSubRules(rawCfg, proxies) + subRules, err := parseSubRules(rawCfg, proxies, ruleProviders) if err != nil { return nil, err } config.SubRules = subRules - rules, err := parseRules(rawCfg.Rule, proxies, subRules, "rules") + rules, err := parseRules(rawCfg.Rule, proxies, ruleProviders, subRules, "rules") if err != nil { return nil, err } @@ -666,7 +674,6 @@ func parseGeneral(cfg *RawConfig) (*General, error) { updater.ExternalUIURL = cfg.ExternalUIURL } - cfg.Tun.RedirectToTun = cfg.EBpf.RedirectToTun return &General{ Inbound: Inbound{ Port: cfg.Port, @@ -845,6 +852,7 @@ func parseListeners(cfg *RawConfig) (listeners map[string]C.InboundListener, err } func parseRuleProviders(cfg *RawConfig) (ruleProviders map[string]providerTypes.RuleProvider, err error) { + RP.SetTunnel(T.Tunnel) ruleProviders = map[string]providerTypes.RuleProvider{} // parse rule provider for name, mapping := range cfg.RuleProvider { @@ -854,12 +862,11 @@ func parseRuleProviders(cfg *RawConfig) (ruleProviders map[string]providerTypes. } ruleProviders[name] = rp - RP.SetRuleProvider(rp) } return } -func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy) (subRules map[string][]C.Rule, err error) { +func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy, ruleProviders map[string]providerTypes.RuleProvider) (subRules map[string][]C.Rule, err error) { subRules = map[string][]C.Rule{} for name := range cfg.SubRules { subRules[name] = make([]C.Rule, 0) @@ -869,7 +876,7 @@ func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy) (subRules map[str return nil, fmt.Errorf("sub-rule name is empty") } var rules []C.Rule - rules, err = parseRules(rawRules, proxies, subRules, fmt.Sprintf("sub-rules[%s]", name)) + rules, err = parseRules(rawRules, proxies, ruleProviders, subRules, fmt.Sprintf("sub-rules[%s]", name)) if err != nil { return nil, err } @@ -922,7 +929,7 @@ func verifySubRuleCircularReferences(n string, subRules map[string][]C.Rule, arr return nil } -func parseRules(rulesConfig []string, proxies map[string]C.Proxy, subRules map[string][]C.Rule, format string) ([]C.Rule, error) { +func parseRules(rulesConfig []string, proxies map[string]C.Proxy, ruleProviders map[string]providerTypes.RuleProvider, subRules map[string][]C.Rule, format string) ([]C.Rule, error) { var rules []C.Rule // parse rules @@ -971,6 +978,12 @@ func parseRules(rulesConfig []string, proxies map[string]C.Proxy, subRules map[s return nil, fmt.Errorf("%s[%d] [%s] error: %s", format, idx, line, parseErr.Error()) } + for _, name := range parsed.ProviderNames() { + if _, ok := ruleProviders[name]; !ok { + return nil, fmt.Errorf("%s[%d] [%s] error: rule set [%s] not found", format, idx, line, name) + } + } + rules = append(rules, parsed) } @@ -1455,31 +1468,39 @@ func parseTun(rawTun RawTun, general *General) error { DNSHijack: rawTun.DNSHijack, AutoRoute: rawTun.AutoRoute, AutoDetectInterface: rawTun.AutoDetectInterface, - RedirectToTun: rawTun.RedirectToTun, - MTU: rawTun.MTU, - GSO: rawTun.GSO, - GSOMaxSize: rawTun.GSOMaxSize, - Inet4Address: []netip.Prefix{tunAddressPrefix}, - Inet6Address: rawTun.Inet6Address, - StrictRoute: rawTun.StrictRoute, + MTU: rawTun.MTU, + GSO: rawTun.GSO, + GSOMaxSize: rawTun.GSOMaxSize, + Inet4Address: []netip.Prefix{tunAddressPrefix}, + Inet6Address: rawTun.Inet6Address, + IPRoute2TableIndex: rawTun.IPRoute2TableIndex, + IPRoute2RuleIndex: rawTun.IPRoute2RuleIndex, + AutoRedirect: rawTun.AutoRedirect, + AutoRedirectInputMark: rawTun.AutoRedirectInputMark, + AutoRedirectOutputMark: rawTun.AutoRedirectOutputMark, + StrictRoute: rawTun.StrictRoute, + RouteAddress: rawTun.RouteAddress, + RouteAddressSet: rawTun.RouteAddressSet, + RouteExcludeAddress: rawTun.RouteExcludeAddress, + RouteExcludeAddressSet: rawTun.RouteExcludeAddressSet, + IncludeInterface: rawTun.IncludeInterface, + ExcludeInterface: rawTun.ExcludeInterface, + IncludeUID: rawTun.IncludeUID, + IncludeUIDRange: rawTun.IncludeUIDRange, + ExcludeUID: rawTun.ExcludeUID, + ExcludeUIDRange: rawTun.ExcludeUIDRange, + IncludeAndroidUser: rawTun.IncludeAndroidUser, + IncludePackage: rawTun.IncludePackage, + ExcludePackage: rawTun.ExcludePackage, + EndpointIndependentNat: rawTun.EndpointIndependentNat, + UDPTimeout: rawTun.UDPTimeout, + FileDescriptor: rawTun.FileDescriptor, + Inet4RouteAddress: rawTun.Inet4RouteAddress, Inet6RouteAddress: rawTun.Inet6RouteAddress, Inet4RouteExcludeAddress: rawTun.Inet4RouteExcludeAddress, Inet6RouteExcludeAddress: rawTun.Inet6RouteExcludeAddress, - IncludeInterface: rawTun.IncludeInterface, - ExcludeInterface: rawTun.ExcludeInterface, - IncludeUID: rawTun.IncludeUID, - IncludeUIDRange: rawTun.IncludeUIDRange, - ExcludeUID: rawTun.ExcludeUID, - ExcludeUIDRange: rawTun.ExcludeUIDRange, - IncludeAndroidUser: rawTun.IncludeAndroidUser, - IncludePackage: rawTun.IncludePackage, - ExcludePackage: rawTun.ExcludePackage, - EndpointIndependentNat: rawTun.EndpointIndependentNat, - UDPTimeout: rawTun.UDPTimeout, - FileDescriptor: rawTun.FileDescriptor, - TableIndex: rawTun.TableIndex, } return nil diff --git a/constant/provider/interface.go b/constant/provider/interface.go index bb73d1bc..f7dfc9cc 100644 --- a/constant/provider/interface.go +++ b/constant/provider/interface.go @@ -84,7 +84,7 @@ type RuleProvider interface { Match(*constant.Metadata) bool ShouldResolveIP() bool ShouldFindProcess() bool - AsRule(adaptor string) constant.Rule + Strategy() any } // Rule Behavior @@ -127,3 +127,9 @@ func (rf RuleFormat) String() string { return "Unknown" } } + +type Tunnel interface { + Providers() map[string]ProxyProvider + RuleProviders() map[string]RuleProvider + RuleUpdateCallback() *utils.Callback[RuleProvider] +} diff --git a/constant/rule.go b/constant/rule.go index 161c200a..a91ee6cb 100644 --- a/constant/rule.go +++ b/constant/rule.go @@ -116,4 +116,5 @@ type Rule interface { Payload() string ShouldResolveIP() bool ShouldFindProcess() bool + ProviderNames() []string } diff --git a/dns/policy.go b/dns/policy.go index a58123e3..fc60401b 100644 --- a/dns/policy.go +++ b/dns/policy.go @@ -37,14 +37,17 @@ func (p geositePolicy) Match(domain string) []dnsClient { } type domainSetPolicy struct { - domainSetProvider provider.RuleProvider - dnsClients []dnsClient + tunnel provider.Tunnel + name string + dnsClients []dnsClient } func (p domainSetPolicy) Match(domain string) []dnsClient { - metadata := &C.Metadata{Host: domain} - if ok := p.domainSetProvider.Match(metadata); ok { - return p.dnsClients + if ruleProvider, ok := p.tunnel.RuleProviders()[p.name]; ok { + metadata := &C.Metadata{Host: domain} + if ok := ruleProvider.Match(metadata); ok { + return p.dnsClients + } } return nil } diff --git a/dns/resolver.go b/dns/resolver.go index 08de69ad..28ffec6f 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -414,7 +414,7 @@ type Config struct { Pool *fakeip.Pool Hosts *trie.DomainTrie[resolver.HostValue] Policy *orderedmap.OrderedMap[string, []NameServer] - RuleProviders map[string]provider.RuleProvider + Tunnel provider.Tunnel CacheAlgorithm string } @@ -502,11 +502,12 @@ func NewResolver(config Config) *Resolver { key := temp[1] switch prefix { case "rule-set": - if p, ok := config.RuleProviders[key]; ok { + if _, ok := config.Tunnel.RuleProviders()[key]; ok { log.Debugln("Adding rule-set policy: %s ", key) insertPolicy(domainSetPolicy{ - domainSetProvider: p, - dnsClients: cacheTransform(nameserver), + tunnel: config.Tunnel, + name: key, + dnsClients: cacheTransform(nameserver), }) continue } else { diff --git a/docs/config.yaml b/docs/config.yaml index 462b3a44..9c51bc10 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -116,13 +116,25 @@ tun: # mtu: 9000 # 最大传输单元 # gso: false # 启用通用分段卸载,仅支持 Linux # gso-max-size: 65536 # 通用分段卸载包的最大大小 + auto-redirect: false # 自动配置 iptables 以重定向 TCP 连接。仅支持 Linux。带有 auto-redirect 的 auto-route 现在可以在路由器上按预期工作,无需干预。 # strict-route: true # 将所有连接路由到 tun 来防止泄漏,但你的设备将无法其他设备被访问 - inet4-route-address: # 启用 auto-route 时使用自定义路由而不是默认路由 + route-address-set: # 将指定规则集中的目标 IP CIDR 规则添加到防火墙, 不匹配的流量将绕过路由, 仅支持 Linux,且需要 nftables,`auto-route` 和 `auto-redirect` 已启用。 + - ruleset-1 + - ruleset-2 + route-exclude-address-set: # 将指定规则集中的目标 IP CIDR 规则添加到防火墙, 匹配的流量将绕过路由, 仅支持 Linux,且需要 nftables,`auto-route` 和 `auto-redirect` 已启用。 + - ruleset-3 + - ruleset-4 + route-address: # 启用 auto-route 时使用自定义路由而不是默认路由 - 0.0.0.0/1 - 128.0.0.0/1 - inet6-route-address: # 启用 auto-route 时使用自定义路由而不是默认路由 - "::/1" - "8000::/1" + # inet4-route-address: # 启用 auto-route 时使用自定义路由而不是默认路由(旧写法) + # - 0.0.0.0/1 + # - 128.0.0.0/1 + # inet6-route-address: # 启用 auto-route 时使用自定义路由而不是默认路由(旧写法) + # - "::/1" + # - "8000::/1" # endpoint-independent-nat: false # 启用独立于端点的 NAT # include-interface: # 限制被路由的接口。默认不限制,与 `exclude-interface` 冲突 # - "lan0" diff --git a/go.mod b/go.mod index 5d109d8d..ee0eeeb4 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 - github.com/metacubex/sing-tun v0.2.7-0.20240521155100-e8316a45a414 + github.com/metacubex/sing-tun v0.2.7-0.20240617013029-d05cf9df9cfe github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 @@ -35,8 +35,8 @@ require ( github.com/oschwald/maxminddb-golang v1.12.0 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.8 + github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a + github.com/sagernet/sing v0.5.0-alpha.10 github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 github.com/sagernet/sing-shadowtls v0.1.4 github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e @@ -47,11 +47,11 @@ require ( github.com/wk8/go-ordered-map/v2 v2.1.8 go.uber.org/automaxprocs v1.5.3 go4.org/netipx v0.0.0-20231129151722-fdeea329fbba - golang.org/x/crypto v0.23.0 - golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 - golang.org/x/net v0.25.0 + golang.org/x/crypto v0.24.0 + golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 + golang.org/x/net v0.26.0 golang.org/x/sync v0.7.0 - golang.org/x/sys v0.20.0 + golang.org/x/sys v0.21.0 google.golang.org/protobuf v1.34.1 gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.3.0 @@ -92,6 +92,7 @@ require ( github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qtls-go1-20 v0.4.1 // indirect + github.com/sagernet/nftables v0.3.0-beta.4 // indirect github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect @@ -100,14 +101,14 @@ require ( github.com/tklauser/go-sysconf v0.3.12 // indirect 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/vishvananda/netns v0.0.4 // 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.4.0 // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/text v0.15.0 // indirect + golang.org/x/mod v0.18.0 // indirect + golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.21.0 // indirect + golang.org/x/tools v0.22.0 // indirect ) -replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1 +replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20240617013425-3e3bd9dab6a2 diff --git a/go.sum b/go.sum index 5c8219c2..a1d0cf5a 100644 --- a/go.sum +++ b/go.sum @@ -108,16 +108,16 @@ github.com/metacubex/quic-go v0.45.1-0.20240610004319-163fee60637e h1:bLYn3GuRvW github.com/metacubex/quic-go v0.45.1-0.20240610004319-163fee60637e/go.mod h1:Yza2H7Ax1rxWPUcJx0vW+oAt9EsPuSiyQFhFabUPzwU= github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs= github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY= -github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1 h1:7hDHLTmjgtRoAp59STwPQpe5Pinwi4cWex+FB3Ohvco= -github.com/metacubex/sing v0.0.0-20240518125217-e63d65a914d1/go.mod h1:+60H3Cm91RnL9dpVGWDPHt0zTQImO9Vfqt9a4rSambI= +github.com/metacubex/sing v0.0.0-20240617013425-3e3bd9dab6a2 h1:N5tidgg/FRmkgPw/AjRwhLUinKDx/ODCSbvv9xqRoLM= +github.com/metacubex/sing v0.0.0-20240617013425-3e3bd9dab6a2/go.mod h1:ARkL0gM13/Iv5VCZmci/NuoOlePoIsW0m7BWfln/Hak= github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 h1:Wr4g1HCb5Z/QIFwFiVNjO2qL+dRu25+Mdn9xtAZZ+ew= github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72/go.mod h1:g7Mxj7b7zm7YVqD975mk/hSmrb0A0G4bVvIMr2MMzn8= 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= github.com/metacubex/sing-shadowsocks2 v0.2.0/go.mod h1:LCKF6j1P94zN8ZS+LXRK1gmYTVGB3squivBSXAFnOg8= -github.com/metacubex/sing-tun v0.2.7-0.20240521155100-e8316a45a414 h1:IPxTZgQV6fVUBS8tozLMSFPHV3imYc/NbuGfp0bLQq0= -github.com/metacubex/sing-tun v0.2.7-0.20240521155100-e8316a45a414/go.mod h1:4VsMwZH1IlgPGFK1ZbBomZ/B2MYkTgs2+gnBAr5GOIo= +github.com/metacubex/sing-tun v0.2.7-0.20240617013029-d05cf9df9cfe h1:NrWjVEkRmEkdREVSpohMgEBoznS0PrRfJDr6iCV4348= +github.com/metacubex/sing-tun v0.2.7-0.20240617013029-d05cf9df9cfe/go.mod h1:WwJGbCx7bQcBzuQXiDOJvZH27R0kIjKNNlISIWsL6kM= 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-20240321042214-224f96122a63 h1:AGyIB55UfQm/0ZH0HtQO9u3l//yjtHUpjeRjjPGfGRI= @@ -159,8 +159,10 @@ github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58 github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0= github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM= -github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= -github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= +github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis= +github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= +github.com/sagernet/nftables v0.3.0-beta.4 h1:kbULlAwAC3jvdGAC1P5Fa3GSxVwQJibNenDW2zaXr8I= +github.com/sagernet/nftables v0.3.0-beta.4/go.mod h1:OQXAjvjNGGFxaTgVCSTRIhYB5/llyVDeapVoENYBDS8= github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6 h1:5bCAkvDDzSMITiHFjolBwpdqYsvycdTu71FsMEFXQ14= github.com/sagernet/sing-mux v0.2.1-0.20240124034317-9bfb33698bb6/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ= github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k= @@ -206,8 +208,8 @@ github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg= -github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8= +github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= 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.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= @@ -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.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= 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.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.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.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= @@ -252,19 +254,18 @@ 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.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= 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.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= 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.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 56e71632..55c40b6d 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -97,7 +97,7 @@ func ApplyConfig(cfg *config.Config, force bool) { updateHosts(cfg.Hosts) updateGeneral(cfg.General) updateNTP(cfg.NTP) - updateDNS(cfg.DNS, cfg.RuleProviders, cfg.General.IPv6) + updateDNS(cfg.DNS, cfg.General.IPv6) updateListeners(cfg.General, cfg.Listeners, force) updateIPTables(cfg) updateTun(cfg.General) @@ -211,7 +211,7 @@ func updateNTP(c *config.NTP) { } } -func updateDNS(c *config.DNS, ruleProvider map[string]provider.RuleProvider, generalIPv6 bool) { +func updateDNS(c *config.DNS, generalIPv6 bool) { if !c.Enable { resolver.DefaultResolver = nil resolver.DefaultHostMapper = nil @@ -237,7 +237,7 @@ func updateDNS(c *config.DNS, ruleProvider map[string]provider.RuleProvider, gen Default: c.DefaultNameserver, Policy: c.NameServerPolicy, ProxyServer: c.ProxyServerNameserver, - RuleProviders: ruleProvider, + Tunnel: tunnel.Tunnel, CacheAlgorithm: c.CacheAlgorithm, } @@ -355,7 +355,7 @@ func updateTun(general *config.General) { return } listener.ReCreateTun(general.Tun, tunnel.Tunnel) - listener.ReCreateRedirToTun(general.Tun.RedirectToTun) + listener.ReCreateRedirToTun(general.EBpf.RedirectToTun) } func updateSniffer(sniffer *config.Sniffer) { @@ -507,9 +507,7 @@ func updateIPTables(cfg *config.Config) { inboundInterface = iptables.InboundInterface } - if dialer.DefaultRoutingMark.Load() == 0 { - dialer.DefaultRoutingMark.Store(2158) - } + dialer.DefaultRoutingMark.CompareAndSwap(0, 2158) err = tproxy.SetTProxyIPTables(inboundInterface, bypass, uint16(tProxyPort), DnsRedirect, dnsPort.Port()) if err != nil { diff --git a/hub/route/configs.go b/hub/route/configs.go index f2cf298a..17d858d4 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -68,25 +68,34 @@ type tunSchema struct { GSO *bool `yaml:"gso" json:"gso,omitempty"` GSOMaxSize *uint32 `yaml:"gso-max-size" json:"gso-max-size,omitempty"` //Inet4Address *[]netip.Prefix `yaml:"inet4-address" json:"inet4-address,omitempty"` - Inet6Address *[]netip.Prefix `yaml:"inet6-address" json:"inet6-address,omitempty"` - StrictRoute *bool `yaml:"strict-route" json:"strict-route,omitempty"` + Inet6Address *[]netip.Prefix `yaml:"inet6-address" json:"inet6-address,omitempty"` + IPRoute2TableIndex *int `yaml:"iproute2-table-index" json:"iproute2_table_index,omitempty"` + IPRoute2RuleIndex *int `yaml:"iproute2-rule-index" json:"iproute2_rule_index,omitempty"` + AutoRedirect *bool `yaml:"auto-redirect" json:"auto_redirect,omitempty"` + AutoRedirectInputMark *uint32 `yaml:"auto-redirect-input-mark" json:"auto_redirect_input_mark,omitempty"` + AutoRedirectOutputMark *uint32 `yaml:"auto-redirect-output-mark" json:"auto_redirect_output_mark,omitempty"` + StrictRoute *bool `yaml:"strict-route" json:"strict-route,omitempty"` + RouteAddress *[]netip.Prefix `yaml:"route-address" json:"route_address,omitempty"` + RouteAddressSet *[]string `yaml:"route-address-set" json:"route_address_set,omitempty"` + RouteExcludeAddress *[]netip.Prefix `yaml:"route-exclude-address" json:"route_exclude_address,omitempty"` + RouteExcludeAddressSet *[]string `yaml:"route-exclude-address-set" json:"route_exclude_address_set,omitempty"` + IncludeInterface *[]string `yaml:"include-interface" json:"include-interface,omitempty"` + ExcludeInterface *[]string `yaml:"exclude-interface" json:"exclude-interface,omitempty"` + IncludeUID *[]uint32 `yaml:"include-uid" json:"include-uid,omitempty"` + IncludeUIDRange *[]string `yaml:"include-uid-range" json:"include-uid-range,omitempty"` + ExcludeUID *[]uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"` + ExcludeUIDRange *[]string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"` + IncludeAndroidUser *[]int `yaml:"include-android-user" json:"include-android-user,omitempty"` + IncludePackage *[]string `yaml:"include-package" json:"include-package,omitempty"` + ExcludePackage *[]string `yaml:"exclude-package" json:"exclude-package,omitempty"` + 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"` + Inet4RouteAddress *[]netip.Prefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"` Inet6RouteAddress *[]netip.Prefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"` Inet4RouteExcludeAddress *[]netip.Prefix `yaml:"inet4-route-exclude-address" json:"inet4-route-exclude-address,omitempty"` Inet6RouteExcludeAddress *[]netip.Prefix `yaml:"inet6-route-exclude-address" json:"inet6-route-exclude-address,omitempty"` - IncludeInterface *[]string `yaml:"include-interface" json:"include-interface,omitempty"` - ExcludeInterface *[]string `yaml:"exclude-interface" json:"exclude-interface,omitempty"` - IncludeUID *[]uint32 `yaml:"include-uid" json:"include-uid,omitempty"` - IncludeUIDRange *[]string `yaml:"include-uid-range" json:"include-uid-range,omitempty"` - ExcludeUID *[]uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"` - ExcludeUIDRange *[]string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"` - IncludeAndroidUser *[]int `yaml:"include-android-user" json:"include-android-user,omitempty"` - IncludePackage *[]string `yaml:"include-package" json:"include-package,omitempty"` - ExcludePackage *[]string `yaml:"exclude-package" json:"exclude-package,omitempty"` - 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 { @@ -157,6 +166,36 @@ func pointerOrDefaultTun(p *tunSchema, def LC.Tun) LC.Tun { if p.Inet6Address != nil { def.Inet6Address = *p.Inet6Address } + if p.IPRoute2TableIndex != nil { + def.IPRoute2TableIndex = *p.IPRoute2TableIndex + } + if p.IPRoute2RuleIndex != nil { + def.IPRoute2RuleIndex = *p.IPRoute2RuleIndex + } + if p.AutoRedirect != nil { + def.AutoRedirect = *p.AutoRedirect + } + if p.AutoRedirectInputMark != nil { + def.AutoRedirectInputMark = *p.AutoRedirectInputMark + } + if p.AutoRedirectOutputMark != nil { + def.AutoRedirectOutputMark = *p.AutoRedirectOutputMark + } + if p.StrictRoute != nil { + def.StrictRoute = *p.StrictRoute + } + if p.RouteAddress != nil { + def.RouteAddress = *p.RouteAddress + } + if p.RouteAddressSet != nil { + def.RouteAddressSet = *p.RouteAddressSet + } + if p.RouteExcludeAddress != nil { + def.RouteExcludeAddress = *p.RouteExcludeAddress + } + if p.RouteExcludeAddressSet != nil { + def.RouteExcludeAddressSet = *p.RouteExcludeAddressSet + } if p.Inet4RouteAddress != nil { def.Inet4RouteAddress = *p.Inet4RouteAddress } @@ -205,9 +244,6 @@ 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 7467e4a6..cea22bfd 100644 --- a/listener/config/tun.go +++ b/listener/config/tun.go @@ -27,27 +27,36 @@ type Tun struct { AutoDetectInterface bool `yaml:"auto-detect-interface" json:"auto-detect-interface"` RedirectToTun []string `yaml:"-" json:"-"` - MTU uint32 `yaml:"mtu" json:"mtu,omitempty"` - GSO bool `yaml:"gso" json:"gso,omitempty"` - GSOMaxSize uint32 `yaml:"gso-max-size" json:"gso-max-size,omitempty"` - Inet4Address []netip.Prefix `yaml:"inet4-address" json:"inet4-address,omitempty"` - Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6-address,omitempty"` - StrictRoute bool `yaml:"strict-route" json:"strict-route,omitempty"` + MTU uint32 `yaml:"mtu" json:"mtu,omitempty"` + GSO bool `yaml:"gso" json:"gso,omitempty"` + GSOMaxSize uint32 `yaml:"gso-max-size" json:"gso-max-size,omitempty"` + Inet4Address []netip.Prefix `yaml:"inet4-address" json:"inet4-address,omitempty"` + Inet6Address []netip.Prefix `yaml:"inet6-address" json:"inet6-address,omitempty"` + IPRoute2TableIndex int `yaml:"iproute2-table-index" json:"iproute2_table_index,omitempty"` + IPRoute2RuleIndex int `yaml:"iproute2-rule-index" json:"iproute2_rule_index,omitempty"` + AutoRedirect bool `yaml:"auto-redirect" json:"auto_redirect,omitempty"` + AutoRedirectInputMark uint32 `yaml:"auto-redirect-input-mark" json:"auto_redirect_input_mark,omitempty"` + AutoRedirectOutputMark uint32 `yaml:"auto-redirect-output-mark" json:"auto_redirect_output_mark,omitempty"` + StrictRoute bool `yaml:"strict-route" json:"strict-route,omitempty"` + RouteAddress []netip.Prefix `yaml:"route-address" json:"route_address,omitempty"` + RouteAddressSet []string `yaml:"route-address-set" json:"route_address_set,omitempty"` + RouteExcludeAddress []netip.Prefix `yaml:"route-exclude-address" json:"route_exclude_address,omitempty"` + RouteExcludeAddressSet []string `yaml:"route-exclude-address-set" json:"route_exclude_address_set,omitempty"` + IncludeInterface []string `yaml:"include-interface" json:"include-interface,omitempty"` + ExcludeInterface []string `yaml:"exclude-interface" json:"exclude-interface,omitempty"` + IncludeUID []uint32 `yaml:"include-uid" json:"include-uid,omitempty"` + IncludeUIDRange []string `yaml:"include-uid-range" json:"include-uid-range,omitempty"` + ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"` + ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"` + IncludeAndroidUser []int `yaml:"include-android-user" json:"include-android-user,omitempty"` + IncludePackage []string `yaml:"include-package" json:"include-package,omitempty"` + ExcludePackage []string `yaml:"exclude-package" json:"exclude-package,omitempty"` + 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"` + Inet4RouteAddress []netip.Prefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"` Inet6RouteAddress []netip.Prefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"` Inet4RouteExcludeAddress []netip.Prefix `yaml:"inet4-route-exclude-address" json:"inet4-route-exclude-address,omitempty"` Inet6RouteExcludeAddress []netip.Prefix `yaml:"inet6-route-exclude-address" json:"inet6-route-exclude-address,omitempty"` - IncludeInterface []string `yaml:"include-interface" json:"include-interface,omitempty"` - ExcludeInterface []string `yaml:"exclude-interface" json:"exclude-interface,omitempty"` - IncludeUID []uint32 `yaml:"include-uid" json:"include-uid,omitempty"` - IncludeUIDRange []string `yaml:"include-uid-range" json:"include-uid-range,omitempty"` - ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"` - ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"` - IncludeAndroidUser []int `yaml:"include-android-user" json:"include-android-user,omitempty"` - IncludePackage []string `yaml:"include-package" json:"include-package,omitempty"` - ExcludePackage []string `yaml:"exclude-package" json:"exclude-package,omitempty"` - 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 51747c46..a950f80d 100644 --- a/listener/inbound/tun.go +++ b/listener/inbound/tun.go @@ -18,29 +18,38 @@ type TunOption struct { AutoRoute bool `inbound:"auto-route,omitempty"` AutoDetectInterface bool `inbound:"auto-detect-interface,omitempty"` - MTU uint32 `inbound:"mtu,omitempty"` - GSO bool `inbound:"gso,omitempty"` - GSOMaxSize uint32 `inbound:"gso-max-size,omitempty"` - Inet4Address []string `inbound:"inet4_address,omitempty"` - Inet6Address []string `inbound:"inet6_address,omitempty"` - StrictRoute bool `inbound:"strict_route,omitempty"` + MTU uint32 `inbound:"mtu,omitempty"` + GSO bool `inbound:"gso,omitempty"` + GSOMaxSize uint32 `inbound:"gso-max-size,omitempty"` + Inet4Address []string `inbound:"inet4_address,omitempty"` + Inet6Address []string `inbound:"inet6_address,omitempty"` + IPRoute2TableIndex int `inbound:"iproute2-table-index"` + IPRoute2RuleIndex int `inbound:"iproute2-rule-index"` + AutoRedirect bool `inbound:"auto-redirect"` + AutoRedirectInputMark uint32 `inbound:"auto-redirect-input-mark"` + AutoRedirectOutputMark uint32 `inbound:"auto-redirect-output-mark"` + StrictRoute bool `inbound:"strict_route,omitempty"` + RouteAddress []string `inbound:"route-address"` + RouteAddressSet []string `inbound:"route-address-set"` + RouteExcludeAddress []string `inbound:"route-exclude-address"` + RouteExcludeAddressSet []string `inbound:"route-exclude-address-set"` + IncludeInterface []string `inbound:"include-interface,omitempty"` + ExcludeInterface []string `inbound:"exclude-interface"` + IncludeUID []uint32 `inbound:"include_uid,omitempty"` + IncludeUIDRange []string `inbound:"include_uid_range,omitempty"` + ExcludeUID []uint32 `inbound:"exclude_uid,omitempty"` + ExcludeUIDRange []string `inbound:"exclude_uid_range,omitempty"` + IncludeAndroidUser []int `inbound:"include_android_user,omitempty"` + IncludePackage []string `inbound:"include_package,omitempty"` + ExcludePackage []string `inbound:"exclude_package,omitempty"` + EndpointIndependentNat bool `inbound:"endpoint_independent_nat,omitempty"` + UDPTimeout int64 `inbound:"udp_timeout,omitempty"` + FileDescriptor int `inbound:"file-descriptor,omitempty"` + Inet4RouteAddress []string `inbound:"inet4_route_address,omitempty"` Inet6RouteAddress []string `inbound:"inet6_route_address,omitempty"` Inet4RouteExcludeAddress []string `inbound:"inet4_route_exclude_address,omitempty"` Inet6RouteExcludeAddress []string `inbound:"inet6_route_exclude_address,omitempty"` - IncludeInterface []string `inbound:"include-interface,omitempty"` - ExcludeInterface []string `inbound:"exclude-interface" json:"exclude-interface,omitempty"` - IncludeUID []uint32 `inbound:"include_uid,omitempty"` - IncludeUIDRange []string `inbound:"include_uid_range,omitempty"` - ExcludeUID []uint32 `inbound:"exclude_uid,omitempty"` - ExcludeUIDRange []string `inbound:"exclude_uid_range,omitempty"` - IncludeAndroidUser []int `inbound:"include_android_user,omitempty"` - IncludePackage []string `inbound:"include_package,omitempty"` - ExcludePackage []string `inbound:"exclude_package,omitempty"` - 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 { @@ -63,6 +72,16 @@ func NewTun(options *TunOption) (*Tun, error) { if !exist { return nil, errors.New("invalid tun stack") } + + routeAddress, err := LC.StringSliceToNetipPrefixSlice(options.RouteAddress) + if err != nil { + return nil, err + } + routeExcludeAddress, err := LC.StringSliceToNetipPrefixSlice(options.RouteExcludeAddress) + if err != nil { + return nil, err + } + inet4Address, err := LC.StringSliceToNetipPrefixSlice(options.Inet4Address) if err != nil { return nil, err @@ -91,35 +110,44 @@ func NewTun(options *TunOption) (*Tun, error) { Base: base, config: options, tun: LC.Tun{ - Enable: true, - Device: options.Device, - Stack: stack, - DNSHijack: options.DNSHijack, - AutoRoute: options.AutoRoute, - AutoDetectInterface: options.AutoDetectInterface, - MTU: options.MTU, - GSO: options.GSO, - GSOMaxSize: options.GSOMaxSize, - Inet4Address: inet4Address, - Inet6Address: inet6Address, - StrictRoute: options.StrictRoute, + Enable: true, + Device: options.Device, + Stack: stack, + DNSHijack: options.DNSHijack, + AutoRoute: options.AutoRoute, + AutoDetectInterface: options.AutoDetectInterface, + MTU: options.MTU, + GSO: options.GSO, + GSOMaxSize: options.GSOMaxSize, + Inet4Address: inet4Address, + Inet6Address: inet6Address, + IPRoute2TableIndex: options.IPRoute2TableIndex, + IPRoute2RuleIndex: options.IPRoute2RuleIndex, + AutoRedirect: options.AutoRedirect, + AutoRedirectInputMark: options.AutoRedirectInputMark, + AutoRedirectOutputMark: options.AutoRedirectOutputMark, + StrictRoute: options.StrictRoute, + RouteAddress: routeAddress, + RouteAddressSet: options.RouteAddressSet, + RouteExcludeAddress: routeExcludeAddress, + RouteExcludeAddressSet: options.RouteExcludeAddressSet, + IncludeInterface: options.IncludeInterface, + ExcludeInterface: options.ExcludeInterface, + IncludeUID: options.IncludeUID, + IncludeUIDRange: options.IncludeUIDRange, + ExcludeUID: options.ExcludeUID, + ExcludeUIDRange: options.ExcludeUIDRange, + IncludeAndroidUser: options.IncludeAndroidUser, + IncludePackage: options.IncludePackage, + ExcludePackage: options.ExcludePackage, + EndpointIndependentNat: options.EndpointIndependentNat, + UDPTimeout: options.UDPTimeout, + FileDescriptor: options.FileDescriptor, + Inet4RouteAddress: inet4RouteAddress, Inet6RouteAddress: inet6RouteAddress, Inet4RouteExcludeAddress: inet4RouteExcludeAddress, Inet6RouteExcludeAddress: inet6RouteExcludeAddress, - IncludeInterface: options.IncludeInterface, - ExcludeInterface: options.ExcludeInterface, - IncludeUID: options.IncludeUID, - IncludeUIDRange: options.IncludeUIDRange, - ExcludeUID: options.ExcludeUID, - ExcludeUIDRange: options.ExcludeUIDRange, - IncludeAndroidUser: options.IncludeAndroidUser, - IncludePackage: options.IncludePackage, - ExcludePackage: options.ExcludePackage, - EndpointIndependentNat: options.EndpointIndependentNat, - UDPTimeout: options.UDPTimeout, - FileDescriptor: options.FileDescriptor, - TableIndex: options.TableIndex, }, }, nil } diff --git a/listener/listener.go b/listener/listener.go index e3506188..76860e0d 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -820,11 +820,15 @@ func hasTunConfigChange(tunConf *LC.Tun) bool { LastTunConf.MTU != tunConf.MTU || LastTunConf.GSO != tunConf.GSO || LastTunConf.GSOMaxSize != tunConf.GSOMaxSize || + LastTunConf.IPRoute2TableIndex != tunConf.IPRoute2TableIndex || + LastTunConf.IPRoute2RuleIndex != tunConf.IPRoute2RuleIndex || + LastTunConf.AutoRedirect != tunConf.AutoRedirect || + LastTunConf.AutoRedirectInputMark != tunConf.AutoRedirectInputMark || + LastTunConf.AutoRedirectOutputMark != tunConf.AutoRedirectOutputMark || LastTunConf.StrictRoute != tunConf.StrictRoute || LastTunConf.EndpointIndependentNat != tunConf.EndpointIndependentNat || LastTunConf.UDPTimeout != tunConf.UDPTimeout || - LastTunConf.FileDescriptor != tunConf.FileDescriptor || - LastTunConf.TableIndex != tunConf.TableIndex { + LastTunConf.FileDescriptor != tunConf.FileDescriptor { return true } @@ -836,6 +840,22 @@ func hasTunConfigChange(tunConf *LC.Tun) bool { return tunConf.DNSHijack[i] < tunConf.DNSHijack[j] }) + sort.Slice(tunConf.RouteAddress, func(i, j int) bool { + return tunConf.RouteAddress[i].String() < tunConf.RouteAddress[j].String() + }) + + sort.Slice(tunConf.RouteAddressSet, func(i, j int) bool { + return tunConf.RouteAddressSet[i] < tunConf.RouteAddressSet[j] + }) + + sort.Slice(tunConf.RouteExcludeAddress, func(i, j int) bool { + return tunConf.RouteExcludeAddress[i].String() < tunConf.RouteExcludeAddress[j].String() + }) + + sort.Slice(tunConf.RouteExcludeAddressSet, func(i, j int) bool { + return tunConf.RouteExcludeAddressSet[i] < tunConf.RouteExcludeAddressSet[j] + }) + sort.Slice(tunConf.Inet4Address, func(i, j int) bool { return tunConf.Inet4Address[i].String() < tunConf.Inet4Address[j].String() }) @@ -897,6 +917,10 @@ func hasTunConfigChange(tunConf *LC.Tun) bool { }) if !slices.Equal(tunConf.DNSHijack, LastTunConf.DNSHijack) || + !slices.Equal(tunConf.RouteAddress, LastTunConf.RouteAddress) || + !slices.Equal(tunConf.RouteAddressSet, LastTunConf.RouteAddressSet) || + !slices.Equal(tunConf.RouteExcludeAddress, LastTunConf.RouteExcludeAddress) || + !slices.Equal(tunConf.RouteExcludeAddressSet, LastTunConf.RouteExcludeAddressSet) || !slices.Equal(tunConf.Inet4Address, LastTunConf.Inet4Address) || !slices.Equal(tunConf.Inet6Address, LastTunConf.Inet6Address) || !slices.Equal(tunConf.Inet4RouteAddress, LastTunConf.Inet4RouteAddress) || diff --git a/listener/sing/sing.go b/listener/sing/sing.go index 4e31faeb..10390e73 100644 --- a/listener/sing/sing.go +++ b/listener/sing/sing.go @@ -198,6 +198,12 @@ func (h *ListenerHandler) NewError(ctx context.Context, err error) { log.Warnln("%s listener get error: %+v", h.Type.String(), err) } +func (h *ListenerHandler) TypeMutation(typ C.Type) *ListenerHandler { + handler := *h + handler.Type = typ + return &handler +} + func ShouldIgnorePacketError(err error) bool { // ignore simple error if E.IsTimeout(err) || E.IsClosed(err) || E.IsCanceled(err) { diff --git a/listener/sing_tun/dns.go b/listener/sing_tun/dns.go index 42926732..505f16ac 100644 --- a/listener/sing_tun/dns.go +++ b/listener/sing_tun/dns.go @@ -8,6 +8,7 @@ import ( "time" "github.com/metacubex/mihomo/component/resolver" + C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/listener/sing" "github.com/metacubex/mihomo/log" @@ -124,3 +125,9 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network. } return h.ListenerHandler.NewPacketConnection(ctx, conn, metadata) } + +func (h *ListenerHandler) TypeMutation(typ C.Type) *ListenerHandler { + handle := *h + handle.ListenerHandler = h.ListenerHandler.TypeMutation(typ) + return &handle +} diff --git a/listener/sing_tun/iface.go b/listener/sing_tun/iface.go new file mode 100644 index 00000000..cc142078 --- /dev/null +++ b/listener/sing_tun/iface.go @@ -0,0 +1,70 @@ +package sing_tun + +import ( + "errors" + "net/netip" + + "github.com/metacubex/mihomo/component/iface" + + "github.com/sagernet/sing/common/control" +) + +type defaultInterfaceFinder struct{} + +var DefaultInterfaceFinder control.InterfaceFinder = (*defaultInterfaceFinder)(nil) + +func (f *defaultInterfaceFinder) Interfaces() []control.Interface { + ifaces, err := iface.Interfaces() + if err != nil { + return nil + } + interfaces := make([]control.Interface, 0, len(ifaces)) + for _, _interface := range ifaces { + interfaces = append(interfaces, control.Interface(*_interface)) + } + + return interfaces +} + +var errNoSuchInterface = errors.New("no such network interface") + +func (f *defaultInterfaceFinder) InterfaceIndexByName(name string) (int, error) { + ifaces, err := iface.Interfaces() + if err != nil { + return 0, err + } + for _, netInterface := range ifaces { + if netInterface.Name == name { + return netInterface.Index, nil + } + } + return 0, errNoSuchInterface +} + +func (f *defaultInterfaceFinder) InterfaceNameByIndex(index int) (string, error) { + ifaces, err := iface.Interfaces() + if err != nil { + return "", err + } + for _, netInterface := range ifaces { + if netInterface.Index == index { + return netInterface.Name, nil + } + } + return "", errNoSuchInterface +} + +func (f *defaultInterfaceFinder) InterfaceByAddr(addr netip.Addr) (*control.Interface, error) { + ifaces, err := iface.Interfaces() + if err != nil { + return nil, err + } + for _, netInterface := range ifaces { + for _, prefix := range netInterface.Addresses { + if prefix.Contains(addr) { + return (*control.Interface)(netInterface), nil + } + } + } + return nil, errNoSuchInterface +} diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index a5edb77f..53b88528 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -3,27 +3,33 @@ package sing_tun import ( "context" "fmt" + "io" "net" "net/netip" + "os" "runtime" "strconv" "strings" + "sync" "github.com/metacubex/mihomo/adapter/inbound" "github.com/metacubex/mihomo/component/dialer" "github.com/metacubex/mihomo/component/iface" "github.com/metacubex/mihomo/component/resolver" C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/constant/provider" LC "github.com/metacubex/mihomo/listener/config" "github.com/metacubex/mihomo/listener/sing" "github.com/metacubex/mihomo/log" tun "github.com/metacubex/sing-tun" "github.com/sagernet/sing/common" - "github.com/sagernet/sing/common/control" E "github.com/sagernet/sing/common/exceptions" F "github.com/sagernet/sing/common/format" "github.com/sagernet/sing/common/ranges" + + "go4.org/netipx" + "golang.org/x/exp/maps" "golang.org/x/exp/slices" ) @@ -43,10 +49,21 @@ type Listener struct { networkUpdateMonitor tun.NetworkUpdateMonitor defaultInterfaceMonitor tun.DefaultInterfaceMonitor packageManager tun.PackageManager + autoRedirect tun.AutoRedirect + autoRedirectOutputMark int32 + + ruleUpdateCallbackCloser io.Closer + ruleUpdateMutex sync.Mutex + routeAddressMap map[string]*netipx.IPSet + routeExcludeAddressMap map[string]*netipx.IPSet + routeAddressSet []*netipx.IPSet + routeExcludeAddressSet []*netipx.IPSet dnsServerIp []string } +var emptyAddressSet = []*netipx.IPSet{{}} + func CalculateInterfaceName(name string) (tunName string) { if runtime.GOOS == "darwin" { tunName = "utun" @@ -110,14 +127,45 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis inbound.WithSpecialRules(""), } } + ctx := context.TODO() + rpTunnel := tunnel.(provider.Tunnel) if options.GSOMaxSize == 0 { options.GSOMaxSize = 65536 } + if runtime.GOOS != "linux" { + options.AutoRedirect = false + } tunName := options.Device if tunName == "" || !checkTunName(tunName) { tunName = CalculateInterfaceName(InterfaceName) options.Device = tunName } + routeAddress := options.RouteAddress + if len(options.Inet4RouteAddress) > 0 { + routeAddress = append(routeAddress, options.Inet4RouteAddress...) + } + if len(options.Inet6RouteAddress) > 0 { + routeAddress = append(routeAddress, options.Inet6RouteAddress...) + } + inet4RouteAddress := common.Filter(routeAddress, func(it netip.Prefix) bool { + return it.Addr().Is4() + }) + inet6RouteAddress := common.Filter(routeAddress, func(it netip.Prefix) bool { + return it.Addr().Is6() + }) + routeExcludeAddress := options.RouteExcludeAddress + if len(options.Inet4RouteExcludeAddress) > 0 { + routeExcludeAddress = append(routeExcludeAddress, options.Inet4RouteExcludeAddress...) + } + if len(options.Inet6RouteExcludeAddress) > 0 { + routeExcludeAddress = append(routeExcludeAddress, options.Inet6RouteExcludeAddress...) + } + inet4RouteExcludeAddress := common.Filter(routeExcludeAddress, func(it netip.Prefix) bool { + return it.Addr().Is4() + }) + inet6RouteExcludeAddress := common.Filter(routeExcludeAddress, func(it netip.Prefix) bool { + return it.Addr().Is6() + }) tunMTU := options.MTU if tunMTU == 0 { tunMTU = 9000 @@ -128,9 +176,21 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis } else { udpTimeout = int64(sing.UDPTimeout.Seconds()) } - tableIndex := options.TableIndex + tableIndex := options.IPRoute2TableIndex if tableIndex == 0 { - tableIndex = 2022 + tableIndex = tun.DefaultIPRoute2TableIndex + } + ruleIndex := options.IPRoute2RuleIndex + if ruleIndex == 0 { + ruleIndex = tun.DefaultIPRoute2RuleIndex + } + inputMark := options.AutoRedirectInputMark + if inputMark == 0 { + inputMark = tun.DefaultAutoRedirectInputMark + } + outputMark := options.AutoRedirectOutputMark + if outputMark == 0 { + outputMark = tun.DefaultAutoRedirectOutputMark } includeUID := uidToRange(options.IncludeUID) if len(options.IncludeUIDRange) > 0 { @@ -202,6 +262,8 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis } }() + interfaceFinder := DefaultInterfaceFinder + networkUpdateMonitor, err := tun.NewNetworkUpdateMonitor(log.SingLogger) if err != nil { err = E.Cause(err, "create NetworkUpdateMonitor") @@ -236,11 +298,15 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis Inet4Address: options.Inet4Address, Inet6Address: options.Inet6Address, AutoRoute: options.AutoRoute, + IPRoute2TableIndex: tableIndex, + IPRoute2RuleIndex: ruleIndex, + AutoRedirectInputMark: inputMark, + AutoRedirectOutputMark: outputMark, StrictRoute: options.StrictRoute, - Inet4RouteAddress: options.Inet4RouteAddress, - Inet6RouteAddress: options.Inet6RouteAddress, - Inet4RouteExcludeAddress: options.Inet4RouteExcludeAddress, - Inet6RouteExcludeAddress: options.Inet6RouteExcludeAddress, + Inet4RouteAddress: inet4RouteAddress, + Inet6RouteAddress: inet6RouteAddress, + Inet4RouteExcludeAddress: inet4RouteExcludeAddress, + Inet6RouteExcludeAddress: inet6RouteExcludeAddress, IncludeInterface: options.IncludeInterface, ExcludeInterface: options.ExcludeInterface, IncludeUID: includeUID, @@ -250,7 +316,56 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis ExcludePackage: options.ExcludePackage, FileDescriptor: options.FileDescriptor, InterfaceMonitor: defaultInterfaceMonitor, - TableIndex: tableIndex, + } + + if options.AutoRedirect { + l.routeAddressMap = make(map[string]*netipx.IPSet) + l.routeExcludeAddressMap = make(map[string]*netipx.IPSet) + + if !options.AutoRoute { + return nil, E.New("`auto-route` is required by `auto-redirect`") + } + disableNFTables, dErr := strconv.ParseBool(os.Getenv("DISABLE_NFTABLES")) + l.autoRedirect, err = tun.NewAutoRedirect(tun.AutoRedirectOptions{ + TunOptions: &tunOptions, + Context: ctx, + Handler: handler.TypeMutation(C.REDIR), + Logger: log.SingLogger, + NetworkMonitor: networkUpdateMonitor, + InterfaceFinder: interfaceFinder, + TableName: "mihomo", + DisableNFTables: dErr == nil && disableNFTables, + RouteAddressSet: &l.routeAddressSet, + RouteExcludeAddressSet: &l.routeExcludeAddressSet, + }) + if err != nil { + err = E.Cause(err, "initialize auto redirect") + return + } + + var markMode bool + for _, routeAddressSet := range options.RouteAddressSet { + rp, loaded := rpTunnel.RuleProviders()[routeAddressSet] + if !loaded { + err = E.New("parse route-address-set: rule-set not found: ", routeAddressSet) + return + } + l.updateRule(rp, false, false) + markMode = true + } + for _, routeExcludeAddressSet := range options.RouteExcludeAddressSet { + rp, loaded := rpTunnel.RuleProviders()[routeExcludeAddressSet] + if !loaded { + err = E.New("parse route-exclude_address-set: rule-set not found: ", routeExcludeAddressSet) + return + } + l.updateRule(rp, true, false) + markMode = true + } + if markMode { + tunOptions.AutoRedirectMarkMode = true + } + } err = l.buildAndroidRules(&tunOptions) @@ -269,14 +384,14 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis resolver.AddSystemDnsBlacklist(dnsServerIp...) stackOptions := tun.StackOptions{ - Context: context.TODO(), + Context: ctx, Tun: tunIf, TunOptions: tunOptions, EndpointIndependentNat: options.EndpointIndependentNat, UDPTimeout: udpTimeout, Handler: handler, Logger: log.SingLogger, - InterfaceFinder: control.DefaultInterfaceFinder(), + InterfaceFinder: interfaceFinder, EnforceBindInterface: EnforceBindInterface, } @@ -299,13 +414,76 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis } l.tunStack = tunStack + if l.autoRedirect != nil { + if len(l.options.RouteAddressSet) > 0 && len(l.routeAddressSet) == 0 { + l.routeAddressSet = emptyAddressSet // without this we can't call UpdateRouteAddressSet after Start + } + if len(l.options.RouteExcludeAddressSet) > 0 && len(l.routeExcludeAddressSet) == 0 { + l.routeExcludeAddressSet = emptyAddressSet // without this we can't call UpdateRouteAddressSet after Start + } + err = l.autoRedirect.Start() + if err != nil { + err = E.Cause(err, "auto redirect") + return + } + if tunOptions.AutoRedirectMarkMode { + l.autoRedirectOutputMark = int32(outputMark) + dialer.DefaultRoutingMark.Store(l.autoRedirectOutputMark) + l.autoRedirect.UpdateRouteAddressSet() + l.ruleUpdateCallbackCloser = rpTunnel.RuleUpdateCallback().Register(l.ruleUpdateCallback) + } + } + //l.openAndroidHotspot(tunOptions) - l.addrStr = fmt.Sprintf("%s(%s,%s), mtu: %d, auto route: %v, ip stack: %s", - tunName, tunOptions.Inet4Address, tunOptions.Inet6Address, tunMTU, options.AutoRoute, options.Stack) + l.addrStr = fmt.Sprintf("%s(%s,%s), mtu: %d, auto route: %v, auto redir: %v, ip stack: %s", + tunName, tunOptions.Inet4Address, tunOptions.Inet6Address, tunMTU, options.AutoRoute, options.AutoRedirect, options.Stack) return } +func (l *Listener) ruleUpdateCallback(ruleProvider provider.RuleProvider) { + name := ruleProvider.Name() + if slices.Contains(l.options.RouteAddressSet, name) { + l.updateRule(ruleProvider, false, true) + return + } + if slices.Contains(l.options.RouteExcludeAddressSet, name) { + l.updateRule(ruleProvider, true, true) + return + } +} + +func (l *Listener) updateRule(ruleProvider provider.RuleProvider, exclude bool, update bool) { + l.ruleUpdateMutex.Lock() + defer l.ruleUpdateMutex.Unlock() + name := ruleProvider.Name() + switch rp := ruleProvider.Strategy().(type) { + case interface{ ToIpCidr() *netipx.IPSet }: + if !exclude { + ipCidr := rp.ToIpCidr() + if ipCidr != nil { + l.routeAddressMap[name] = ipCidr + } else { + delete(l.routeAddressMap, name) + } + l.routeAddressSet = maps.Values(l.routeAddressMap) + } else { + ipCidr := rp.ToIpCidr() + if ipCidr != nil { + l.routeExcludeAddressMap[name] = ipCidr + } else { + delete(l.routeExcludeAddressMap, name) + } + l.routeExcludeAddressSet = maps.Values(l.routeExcludeAddressMap) + } + default: + return + } + if update && l.autoRedirect != nil { + l.autoRedirect.UpdateRouteAddressSet() + } +} + func (l *Listener) FlushDefaultInterface() { if l.options.AutoDetectInterface { for _, destination := range []netip.Addr{netip.IPv4Unspecified(), netip.IPv6Unspecified(), netip.MustParseAddr("1.1.1.1")} { @@ -347,11 +525,11 @@ func parseRange(uidRanges []ranges.Range[uint32], rangeList []string) ([]ranges. } var start, end uint64 var err error - start, err = strconv.ParseUint(uidRange[:subIndex], 10, 32) + start, err = strconv.ParseUint(uidRange[:subIndex], 0, 32) if err != nil { return nil, E.Cause(err, "parse range start") } - end, err = strconv.ParseUint(uidRange[subIndex+1:], 10, 32) + end, err = strconv.ParseUint(uidRange[subIndex+1:], 0, 32) if err != nil { return nil, E.Cause(err, "parse range end") } @@ -363,9 +541,14 @@ func parseRange(uidRanges []ranges.Range[uint32], rangeList []string) ([]ranges. func (l *Listener) Close() error { l.closed = true resolver.RemoveSystemDnsBlacklist(l.dnsServerIp...) + if l.autoRedirectOutputMark != 0 { + dialer.DefaultRoutingMark.CompareAndSwap(l.autoRedirectOutputMark, 0) + } return common.Close( + l.ruleUpdateCallbackCloser, l.tunStack, l.tunIf, + l.autoRedirect, l.defaultInterfaceMonitor, l.networkUpdateMonitor, l.packageManager, diff --git a/listener/tproxy/tproxy_iptables.go b/listener/tproxy/tproxy_iptables.go index 6c6e2cc8..bc26b125 100644 --- a/listener/tproxy/tproxy_iptables.go +++ b/listener/tproxy/tproxy_iptables.go @@ -119,9 +119,7 @@ func CleanupTProxyIPTables() { log.Warnln("Cleanup tproxy linux iptables") - if int(dialer.DefaultRoutingMark.Load()) == 2158 { - dialer.DefaultRoutingMark.Store(0) - } + dialer.DefaultRoutingMark.CompareAndSwap(2158, 0) if _, err := cmd.ExecCmd("iptables -t mangle -L mihomo_divert"); err != nil { return diff --git a/rules/common/base.go b/rules/common/base.go index d912107c..670df1d9 100644 --- a/rules/common/base.go +++ b/rules/common/base.go @@ -20,6 +20,8 @@ func (b *Base) ShouldResolveIP() bool { return false } +func (b *Base) ProviderNames() []string { return nil } + func HasNoResolve(params []string) bool { for _, p := range params { if p == noResolve { diff --git a/rules/logic/logic.go b/rules/logic/logic.go index af8c31a4..397a16b7 100644 --- a/rules/logic/logic.go +++ b/rules/logic/logic.go @@ -298,3 +298,10 @@ func (logic *Logic) ShouldResolveIP() bool { func (logic *Logic) ShouldFindProcess() bool { return logic.needProcess } + +func (logic *Logic) ProviderNames() (names []string) { + for _, rule := range logic.rules { + names = append(names, rule.ProviderNames()...) + } + return +} diff --git a/rules/provider/ipcidr_strategy.go b/rules/provider/ipcidr_strategy.go index c93facd9..d0545c7c 100644 --- a/rules/provider/ipcidr_strategy.go +++ b/rules/provider/ipcidr_strategy.go @@ -4,6 +4,8 @@ import ( "github.com/metacubex/mihomo/component/cidr" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" + + "go4.org/netipx" ) type ipcidrStrategy struct { @@ -52,6 +54,10 @@ func (i *ipcidrStrategy) FinishInsert() { i.cidrSet.Merge() } +func (i *ipcidrStrategy) ToIpCidr() *netipx.IPSet { + return i.cidrSet.ToIPSet() +} + func NewIPCidrStrategy() *ipcidrStrategy { return &ipcidrStrategy{} } diff --git a/rules/provider/patch_android.go b/rules/provider/patch_android.go index 2bd5ffc8..7ef1df1b 100644 --- a/rules/provider/patch_android.go +++ b/rules/provider/patch_android.go @@ -12,8 +12,8 @@ type UpdatableProvider interface { UpdatedAt() time.Time } -func (f *ruleSetProvider) UpdatedAt() time.Time { - return f.Fetcher.UpdatedAt +func (rp *ruleSetProvider) UpdatedAt() time.Time { + return rp.Fetcher.UpdatedAt } func (rp *ruleSetProvider) Close() error { diff --git a/rules/provider/provider.go b/rules/provider/provider.go index adc2e44a..7616290f 100644 --- a/rules/provider/provider.go +++ b/rules/provider/provider.go @@ -15,12 +15,14 @@ import ( P "github.com/metacubex/mihomo/constant/provider" ) -var ( - ruleProviders = map[string]P.RuleProvider{} -) +var tunnel P.Tunnel + +func SetTunnel(t P.Tunnel) { + tunnel = t +} type ruleSetProvider struct { - *resource.Fetcher[any] + *resource.Fetcher[ruleStrategy] behavior P.RuleBehavior format P.RuleFormat strategy ruleStrategy @@ -49,16 +51,6 @@ type ruleStrategy interface { FinishInsert() } -func RuleProviders() map[string]P.RuleProvider { - return ruleProviders -} - -func SetRuleProvider(ruleProvider P.RuleProvider) { - if ruleProvider != nil { - ruleProviders[(ruleProvider).Name()] = ruleProvider - } -} - func (rp *ruleSetProvider) Type() P.ProviderType { return P.Rule } @@ -99,8 +91,8 @@ func (rp *ruleSetProvider) ShouldFindProcess() bool { return rp.strategy.ShouldFindProcess() } -func (rp *ruleSetProvider) AsRule(adaptor string) C.Rule { - panic("implement me") +func (rp *ruleSetProvider) Strategy() any { + return rp.strategy } func (rp *ruleSetProvider) MarshalJSON() ([]byte, error) { @@ -123,13 +115,15 @@ func NewRuleSetProvider(name string, behavior P.RuleBehavior, format P.RuleForma format: format, } - onUpdate := func(elm interface{}) { - strategy := elm.(ruleStrategy) + onUpdate := func(strategy ruleStrategy) { rp.strategy = strategy + tunnel.RuleUpdateCallback().Emit(rp) } rp.strategy = newStrategy(behavior, parse) - rp.Fetcher = resource.NewFetcher(name, interval, vehicle, func(bytes []byte) (any, error) { return rulesParse(bytes, newStrategy(behavior, parse), format) }, onUpdate) + rp.Fetcher = resource.NewFetcher(name, interval, vehicle, func(bytes []byte) (ruleStrategy, error) { + return rulesParse(bytes, newStrategy(behavior, parse), format) + }, onUpdate) wrapper := &RuleSetProvider{ rp, @@ -158,7 +152,7 @@ func newStrategy(behavior P.RuleBehavior, parse func(tp, payload, target string, var ErrNoPayload = errors.New("file must have a `payload` field") -func rulesParse(buf []byte, strategy ruleStrategy, format P.RuleFormat) (any, error) { +func rulesParse(buf []byte, strategy ruleStrategy, format P.RuleFormat) (ruleStrategy, error) { strategy.Reset() schema := &RulePayload{} diff --git a/rules/provider/rule_set.go b/rules/provider/rule_set.go index 1d940188..d85db805 100644 --- a/rules/provider/rule_set.go +++ b/rules/provider/rule_set.go @@ -1,7 +1,6 @@ package provider import ( - "fmt" C "github.com/metacubex/mihomo/constant" P "github.com/metacubex/mihomo/constant/provider" "github.com/metacubex/mihomo/rules/common" @@ -11,13 +10,18 @@ type RuleSet struct { *common.Base ruleProviderName string adapter string - ruleProvider P.RuleProvider noResolveIP bool shouldFindProcess bool } func (rs *RuleSet) ShouldFindProcess() bool { - return rs.shouldFindProcess || rs.getProviders().ShouldFindProcess() + if rs.shouldFindProcess { + return true + } + if provider, ok := rs.getProvider(); ok { + return provider.ShouldFindProcess() + } + return false } func (rs *RuleSet) RuleType() C.RuleType { @@ -25,7 +29,10 @@ func (rs *RuleSet) RuleType() C.RuleType { } func (rs *RuleSet) Match(metadata *C.Metadata) (bool, string) { - return rs.getProviders().Match(metadata), rs.adapter + if provider, ok := rs.getProvider(); ok { + return provider.Match(metadata), rs.adapter + } + return false, "" } func (rs *RuleSet) Adapter() string { @@ -33,31 +40,37 @@ func (rs *RuleSet) Adapter() string { } func (rs *RuleSet) Payload() string { - return rs.getProviders().Name() + if provider, ok := rs.getProvider(); ok { + return provider.Name() + } + return "" } func (rs *RuleSet) ShouldResolveIP() bool { - return !rs.noResolveIP && rs.getProviders().ShouldResolveIP() -} -func (rs *RuleSet) getProviders() P.RuleProvider { - if rs.ruleProvider == nil { - rp := RuleProviders()[rs.ruleProviderName] - rs.ruleProvider = rp + if rs.noResolveIP { + return false } + if provider, ok := rs.getProvider(); ok { + return provider.ShouldResolveIP() + } + return false +} - return rs.ruleProvider +func (rs *RuleSet) ProviderNames() []string { + return []string{rs.ruleProviderName} +} + +func (rs *RuleSet) getProvider() (P.RuleProvider, bool) { + pp, ok := tunnel.RuleProviders()[rs.ruleProviderName] + return pp, ok } func NewRuleSet(ruleProviderName string, adapter string, noResolveIP bool) (*RuleSet, error) { - rp, ok := RuleProviders()[ruleProviderName] - if !ok { - return nil, fmt.Errorf("rule set %s not found", ruleProviderName) - } - return &RuleSet{ + rs := &RuleSet{ Base: &common.Base{}, ruleProviderName: ruleProviderName, adapter: adapter, - ruleProvider: rp, noResolveIP: noResolveIP, - }, nil + } + return rs, nil } diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 2c1b894f..1a6f104d 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -13,6 +13,7 @@ import ( "time" N "github.com/metacubex/mihomo/common/net" + "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/loopback" "github.com/metacubex/mihomo/component/nat" P "github.com/metacubex/mihomo/component/process" @@ -50,11 +51,15 @@ var ( findProcessMode P.FindProcessMode fakeIPRange netip.Prefix + + ruleUpdateCallback = utils.NewCallback[provider.RuleProvider]() ) type tunnel struct{} -var Tunnel C.Tunnel = tunnel{} +var Tunnel = tunnel{} +var _ C.Tunnel = Tunnel +var _ provider.Tunnel = Tunnel func (t tunnel) HandleTCPConn(conn net.Conn, metadata *C.Metadata) { connCtx := icontext.NewConnContext(conn, metadata) @@ -73,6 +78,18 @@ func (t tunnel) NatTable() C.NatTable { return natTable } +func (t tunnel) Providers() map[string]provider.ProxyProvider { + return providers +} + +func (t tunnel) RuleProviders() map[string]provider.RuleProvider { + return ruleProviders +} + +func (t tunnel) RuleUpdateCallback() *utils.Callback[provider.RuleProvider] { + return ruleUpdateCallback +} + func OnSuspend() { status.Store(Suspend) } From 5ab8154e7e1e2aad431a5af358c804b81926853b Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 18 Jun 2024 10:30:43 +0800 Subject: [PATCH 31/38] fix: wireguard ip update --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ee0eeeb4..6030dd1e 100644 --- a/go.mod +++ b/go.mod @@ -26,7 +26,7 @@ require ( github.com/metacubex/sing-shadowsocks2 v0.2.0 github.com/metacubex/sing-tun v0.2.7-0.20240617013029-d05cf9df9cfe github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f - github.com/metacubex/sing-wireguard v0.0.0-20240321042214-224f96122a63 + github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 github.com/metacubex/utls v1.6.6 github.com/miekg/dns v1.1.59 diff --git a/go.sum b/go.sum index a1d0cf5a..7f2f105f 100644 --- a/go.sum +++ b/go.sum @@ -120,8 +120,8 @@ github.com/metacubex/sing-tun v0.2.7-0.20240617013029-d05cf9df9cfe h1:NrWjVEkRmE github.com/metacubex/sing-tun v0.2.7-0.20240617013029-d05cf9df9cfe/go.mod h1:WwJGbCx7bQcBzuQXiDOJvZH27R0kIjKNNlISIWsL6kM= 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-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/sing-wireguard v0.0.0-20240618022557-a6efaa37127a h1:NpSGclHJUYndUwBmyIpFBSoBVg8PoVX7QQKhYg0DjM0= +github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a/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/metacubex/utls v1.6.6 h1:3D12YKHTf2Z41UPhQU2dWerNWJ5TVQD9gKoQ+H+iLC8= From 1457f83530a3d386f49c29c1bb0e846e6350e11a Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 18 Jun 2024 13:12:44 +0800 Subject: [PATCH 32/38] fix: dns server using direct outbound lookback resolve problem --- adapter/outbound/direct.go | 4 ++++ component/dialer/dialer.go | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go index c904d3b6..7114045d 100644 --- a/adapter/outbound/direct.go +++ b/adapter/outbound/direct.go @@ -64,6 +64,10 @@ func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, return d.loopBack.NewPacketConn(newPacketConn(pc, d)), nil } +func (d *Direct) IsL3Protocol(metadata *C.Metadata) bool { + return true // tell DNSDialer don't send domain to DialContext, avoid lookback to DefaultResolver +} + func NewDirectWithOption(option DirectOption) *Direct { return &Direct{ Base: &Base{ diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index c21e638e..54a1aa6a 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -378,12 +378,12 @@ func (d Dialer) DialContext(ctx context.Context, network, address string) (net.C } func (d Dialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) { - opt := WithOption(d.Opt) + opt := d.Opt // make a copy if rAddrPort.Addr().Unmap().IsLoopback() { // avoid "The requested address is not valid in its context." - opt = WithInterface("") + WithInterface("")(&opt) } - return ListenPacket(ctx, ParseNetwork(network, rAddrPort.Addr()), address, rAddrPort, opt) + return ListenPacket(ctx, ParseNetwork(network, rAddrPort.Addr()), address, rAddrPort, WithOption(opt)) } func NewDialer(options ...Option) Dialer { From 917c5fdd80e760603650181190fa3c763a039622 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 19 Jun 2024 10:46:44 +0800 Subject: [PATCH 33/38] fix: auto-redirect android rules --- go.mod | 2 +- go.sum | 4 ++-- listener/sing_tun/redirect_linux.go | 3 +++ listener/sing_tun/redirect_stub.go | 5 +++++ listener/sing_tun/server.go | 8 ++++++-- 5 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 listener/sing_tun/redirect_linux.go create mode 100644 listener/sing_tun/redirect_stub.go diff --git a/go.mod b/go.mod index 6030dd1e..3361adf9 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 - github.com/metacubex/sing-tun v0.2.7-0.20240617013029-d05cf9df9cfe + github.com/metacubex/sing-tun v0.2.7-0.20240619023810-d442c40abab0 github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 diff --git a/go.sum b/go.sum index 7f2f105f..95e0404f 100644 --- a/go.sum +++ b/go.sum @@ -116,8 +116,8 @@ github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwV github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg= github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A= github.com/metacubex/sing-shadowsocks2 v0.2.0/go.mod h1:LCKF6j1P94zN8ZS+LXRK1gmYTVGB3squivBSXAFnOg8= -github.com/metacubex/sing-tun v0.2.7-0.20240617013029-d05cf9df9cfe h1:NrWjVEkRmEkdREVSpohMgEBoznS0PrRfJDr6iCV4348= -github.com/metacubex/sing-tun v0.2.7-0.20240617013029-d05cf9df9cfe/go.mod h1:WwJGbCx7bQcBzuQXiDOJvZH27R0kIjKNNlISIWsL6kM= +github.com/metacubex/sing-tun v0.2.7-0.20240619023810-d442c40abab0 h1:J7YWMrEaYM9WF4qG8ZaCCHGw/ylbZc8FvIHr4rdOzP8= +github.com/metacubex/sing-tun v0.2.7-0.20240619023810-d442c40abab0/go.mod h1:WwJGbCx7bQcBzuQXiDOJvZH27R0kIjKNNlISIWsL6kM= 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-20240618022557-a6efaa37127a h1:NpSGclHJUYndUwBmyIpFBSoBVg8PoVX7QQKhYg0DjM0= diff --git a/listener/sing_tun/redirect_linux.go b/listener/sing_tun/redirect_linux.go new file mode 100644 index 00000000..6ef6fc96 --- /dev/null +++ b/listener/sing_tun/redirect_linux.go @@ -0,0 +1,3 @@ +package sing_tun + +const supportRedirect = true diff --git a/listener/sing_tun/redirect_stub.go b/listener/sing_tun/redirect_stub.go new file mode 100644 index 00000000..d711af3c --- /dev/null +++ b/listener/sing_tun/redirect_stub.go @@ -0,0 +1,5 @@ +//go:build !linux + +package sing_tun + +const supportRedirect = false diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 53b88528..e8c2ad28 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -132,7 +132,7 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis if options.GSOMaxSize == 0 { options.GSOMaxSize = 65536 } - if runtime.GOOS != "linux" { + if !supportRedirect { options.AutoRedirect = false } tunName := options.Device @@ -453,12 +453,16 @@ func (l *Listener) ruleUpdateCallback(ruleProvider provider.RuleProvider) { } } +type toIpCidr interface { + ToIpCidr() *netipx.IPSet +} + func (l *Listener) updateRule(ruleProvider provider.RuleProvider, exclude bool, update bool) { l.ruleUpdateMutex.Lock() defer l.ruleUpdateMutex.Unlock() name := ruleProvider.Name() switch rp := ruleProvider.Strategy().(type) { - case interface{ ToIpCidr() *netipx.IPSet }: + case toIpCidr: if !exclude { ipCidr := rp.ToIpCidr() if ipCidr != nil { From 50286678bf51e9331a2e88eaa522687daaa4242e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 22 Jun 2024 13:08:15 +0800 Subject: [PATCH 34/38] fix: auto-redirect rule error --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3361adf9..8dea8e9a 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 - github.com/metacubex/sing-tun v0.2.7-0.20240619023810-d442c40abab0 + github.com/metacubex/sing-tun v0.2.7-0.20240622050320-d74a7240f063 github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 diff --git a/go.sum b/go.sum index 95e0404f..95c71fed 100644 --- a/go.sum +++ b/go.sum @@ -116,8 +116,8 @@ github.com/metacubex/sing-shadowsocks v0.2.6 h1:6oEB3QcsFYnNiFeoevcXrCwJ3sAablwV github.com/metacubex/sing-shadowsocks v0.2.6/go.mod h1:zIkMeSnb8Mbf4hdqhw0pjzkn1d99YJ3JQm/VBg5WMTg= github.com/metacubex/sing-shadowsocks2 v0.2.0 h1:hqwT/AfI5d5UdPefIzR6onGHJfDXs5zgOM5QSgaM/9A= github.com/metacubex/sing-shadowsocks2 v0.2.0/go.mod h1:LCKF6j1P94zN8ZS+LXRK1gmYTVGB3squivBSXAFnOg8= -github.com/metacubex/sing-tun v0.2.7-0.20240619023810-d442c40abab0 h1:J7YWMrEaYM9WF4qG8ZaCCHGw/ylbZc8FvIHr4rdOzP8= -github.com/metacubex/sing-tun v0.2.7-0.20240619023810-d442c40abab0/go.mod h1:WwJGbCx7bQcBzuQXiDOJvZH27R0kIjKNNlISIWsL6kM= +github.com/metacubex/sing-tun v0.2.7-0.20240622050320-d74a7240f063 h1:l0kqvpBCy1yUAlr79qc0w70nbELJiqs+tWH61b5poiU= +github.com/metacubex/sing-tun v0.2.7-0.20240622050320-d74a7240f063/go.mod h1:WwJGbCx7bQcBzuQXiDOJvZH27R0kIjKNNlISIWsL6kM= 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-20240618022557-a6efaa37127a h1:NpSGclHJUYndUwBmyIpFBSoBVg8PoVX7QQKhYg0DjM0= From a9ecc627e6376bcb75d2dfc9ca20008e8c794f7b Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 22 Jun 2024 13:10:09 +0800 Subject: [PATCH 35/38] fix: subrule can't recursion correctly (#1339) --- rules/logic/logic.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rules/logic/logic.go b/rules/logic/logic.go index 397a16b7..8c79cab5 100644 --- a/rules/logic/logic.go +++ b/rules/logic/logic.go @@ -2,12 +2,13 @@ package logic import ( "fmt" - list "github.com/bahlo/generic-list-go" "regexp" "strings" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/rules/common" + + list "github.com/bahlo/generic-list-go" ) type Logic struct { @@ -243,7 +244,7 @@ func matchSubRules(metadata *C.Metadata, name string, subRules map[string][]C.Ru for _, rule := range subRules[name] { if m, a := rule.Match(metadata); m { if rule.RuleType() == C.SubRules { - matchSubRules(metadata, rule.Adapter(), subRules) + return matchSubRules(metadata, rule.Adapter(), subRules) } else { return m, a } From 9f4cd646c23a8fd511c21446ed2c4d391a21b341 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Sun, 23 Jun 2024 15:33:38 +0800 Subject: [PATCH 36/38] fix: `dhcp://` with special notation cannot be parsed --- component/process/process_linux.go | 1 - config/config.go | 14 +++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/component/process/process_linux.go b/component/process/process_linux.go index 4667104c..45c89e5a 100644 --- a/component/process/process_linux.go +++ b/component/process/process_linux.go @@ -209,7 +209,6 @@ func findPackageName(uid uint32) string { }) if sharedPackage, loaded := packageManager.SharedPackageByID(uid % 100000); loaded { - fmt.Println(loaded) return sharedPackage } if packageName, loaded := packageManager.PackageByID(uid % 100000); loaded { diff --git a/config/config.go b/config/config.go index 2166b87d..5676f7aa 100644 --- a/config/config.go +++ b/config/config.go @@ -1057,6 +1057,16 @@ func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns. var nameservers []dns.NameServer for idx, server := range servers { + if strings.HasPrefix(server, "dhcp://") { + nameservers = append( + nameservers, + dns.NameServer{ + Net: "dhcp", + Addr: server[len("dhcp://"):], + }, + ) + continue + } server = parsePureDNSServer(server) u, err := url.Parse(server) if err != nil { @@ -1099,9 +1109,6 @@ func parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns. } } } - case "dhcp": - addr = u.Host - dnsNetType = "dhcp" // UDP from DHCP case "quic": addr, err = hostWithDefaultPort(u.Host, "853") dnsNetType = "quic" // DNS over QUIC @@ -1174,6 +1181,7 @@ func parsePureDNSServer(server string) string { } } } + func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], ruleProviders map[string]providerTypes.RuleProvider, respectRules bool, preferH3 bool) (*orderedmap.OrderedMap[string, []dns.NameServer], error) { policy := orderedmap.New[string, []dns.NameServer]() updatedPolicy := orderedmap.New[string, any]() From f45ccc07616cfd3ee6dbc1270f7c298cceeb8d04 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 27 Jun 2024 09:51:52 +0800 Subject: [PATCH 37/38] chore: update dependencies --- go.mod | 16 ++++++++-------- go.sum | 35 ++++++++++++++++------------------- 2 files changed, 24 insertions(+), 27 deletions(-) diff --git a/go.mod b/go.mod index 8dea8e9a..4a96d142 100644 --- a/go.mod +++ b/go.mod @@ -9,13 +9,13 @@ require ( github.com/cilium/ebpf v0.12.3 github.com/coreos/go-iptables v0.7.0 github.com/dlclark/regexp2 v1.11.0 - github.com/go-chi/chi/v5 v5.0.12 + github.com/go-chi/chi/v5 v5.0.14 github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.3 github.com/gobwas/ws v1.4.0 github.com/gofrs/uuid/v5 v5.2.0 - github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49 - github.com/klauspost/cpuid/v2 v2.2.7 + github.com/insomniacslk/dhcp v0.0.0-20240529192340-51bc6136a0a6 + github.com/klauspost/cpuid/v2 v2.2.8 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 @@ -24,16 +24,16 @@ require ( github.com/metacubex/sing-quic v0.0.0-20240518034124-7696d3f7da72 github.com/metacubex/sing-shadowsocks v0.2.6 github.com/metacubex/sing-shadowsocks2 v0.2.0 - github.com/metacubex/sing-tun v0.2.7-0.20240622050320-d74a7240f063 + github.com/metacubex/sing-tun v0.2.7-0.20240627012306-9d1f5fc0b45e github.com/metacubex/sing-vmess v0.1.9-0.20231207122118-72303677451f github.com/metacubex/sing-wireguard v0.0.0-20240618022557-a6efaa37127a github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 github.com/metacubex/utls v1.6.6 - github.com/miekg/dns v1.1.59 + github.com/miekg/dns v1.1.61 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.1.0 + github.com/puzpuzpuz/xsync/v3 v3.2.0 github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a github.com/sagernet/sing v0.5.0-alpha.10 @@ -41,7 +41,7 @@ require ( github.com/sagernet/sing-shadowtls v0.1.4 github.com/sagernet/wireguard-go v0.0.0-20231209092712-9a439356a62e github.com/samber/lo v1.39.0 - github.com/shirou/gopsutil/v3 v3.24.4 + github.com/shirou/gopsutil/v3 v3.24.5 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.9.0 github.com/wk8/go-ordered-map/v2 v2.1.8 @@ -52,7 +52,7 @@ require ( golang.org/x/net v0.26.0 golang.org/x/sync v0.7.0 golang.org/x/sys v0.21.0 - google.golang.org/protobuf v1.34.1 + google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.3.0 ) diff --git a/go.sum b/go.sum index 95c71fed..29a5d385 100644 --- a/go.sum +++ b/go.sum @@ -44,8 +44,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk= github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI= -github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= -github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.0.14 h1:PyEwo2Vudraa0x/Wl6eDRRW2NXBvekgfxyydcM0WGE0= +github.com/go-chi/chi/v5 v5.0.14/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= @@ -69,7 +69,6 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= @@ -78,16 +77,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-20240419123447-f1cffa2c0c49 h1:/OuvSMGT9+xnyZ+7MZQ1zdngaCCAdPoSw8B/uurZ7pg= -github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic= +github.com/insomniacslk/dhcp v0.0.0-20240529192340-51bc6136a0a6 h1:dh8D8FksyMhD64mRMbUhZHWYJfNoNMCxfVq6eexleMw= +github.com/insomniacslk/dhcp v0.0.0-20240529192340-51bc6136a0a6/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic= 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.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= -github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= +github.com/klauspost/cpuid/v2 v2.2.8/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= @@ -116,8 +115,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.7-0.20240622050320-d74a7240f063 h1:l0kqvpBCy1yUAlr79qc0w70nbELJiqs+tWH61b5poiU= -github.com/metacubex/sing-tun v0.2.7-0.20240622050320-d74a7240f063/go.mod h1:WwJGbCx7bQcBzuQXiDOJvZH27R0kIjKNNlISIWsL6kM= +github.com/metacubex/sing-tun v0.2.7-0.20240627012306-9d1f5fc0b45e h1:o+zohxPRo45P35fS9u1zfdBgr+L/7S0ObGU6YjbVBIc= +github.com/metacubex/sing-tun v0.2.7-0.20240627012306-9d1f5fc0b45e/go.mod h1:WwJGbCx7bQcBzuQXiDOJvZH27R0kIjKNNlISIWsL6kM= 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-20240618022557-a6efaa37127a h1:NpSGclHJUYndUwBmyIpFBSoBVg8PoVX7QQKhYg0DjM0= @@ -126,8 +125,8 @@ github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66 h1:as/aO/fM8nv4W4 github.com/metacubex/tfo-go v0.0.0-20240228025757-be1269474a66/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts= github.com/metacubex/utls v1.6.6 h1:3D12YKHTf2Z41UPhQU2dWerNWJ5TVQD9gKoQ+H+iLC8= github.com/metacubex/utls v1.6.6/go.mod h1:+WLFUnXjcpdxXCnyX25nggw8C6YonZ8zOK2Zm/oRvdo= -github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs= -github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk= +github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs= +github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ= 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= @@ -150,8 +149,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.1.0 h1:EewKT7/LNac5SLiEblJeUu8z5eERHrmRLnMQL2d7qX4= -github.com/puzpuzpuz/xsync/v3 v3.1.0/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= +github.com/puzpuzpuz/xsync/v3 v3.2.0 h1:9AzuUeF88YC5bK8u2vEG1Fpvu4wgpM1wfPIExfaaDxQ= +github.com/puzpuzpuz/xsync/v3 v3.2.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= @@ -173,12 +172,11 @@ 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/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU= -github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8= +github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= +github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= 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= -github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b h1:rXHg9GrUEtWZhEkrykicdND3VPjlVbYiLdX9J7gimS8= github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b/go.mod h1:X7qrxNQViEaAN9LNZOPl9PfvQtp3V3c7LTo0dvGi0fM= github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c h1:DjKMC30y6yjG3IxDaeAj3PCoRr+IsO+bzyT+Se2m2Hk= @@ -254,7 +252,6 @@ 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.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= @@ -268,8 +265,8 @@ golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= 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.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= 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 0e228765fce4d709af1e672426dea5294e6b7544 Mon Sep 17 00:00:00 2001 From: xishang0128 Date: Fri, 28 Jun 2024 14:14:36 +0800 Subject: [PATCH 38/38] fix: Make the ruleset take effect in a single line --- rules/provider/provider.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rules/provider/provider.go b/rules/provider/provider.go index 7616290f..6c03c6e5 100644 --- a/rules/provider/provider.go +++ b/rules/provider/provider.go @@ -4,11 +4,12 @@ import ( "bytes" "encoding/json" "errors" - "gopkg.in/yaml.v3" "runtime" "strings" "time" + "gopkg.in/yaml.v3" + "github.com/metacubex/mihomo/common/pool" "github.com/metacubex/mihomo/component/resource" C "github.com/metacubex/mihomo/constant" @@ -170,15 +171,14 @@ func rulesParse(buf []byte, strategy ruleStrategy, format P.RuleFormat) (ruleStr line = buf[s : i+1] s = i + 1 } else { - s = len(buf) // stop loop in next step - if firstLineLength == 0 { // no head or only one line body + s = len(buf) // stop loop in next step + if firstLineLength == 0 && format == P.YamlRule { // no head or only one line body return nil, ErrNoPayload } } var str string switch format { case P.TextRule: - firstLineLength = -1 // don't return ErrNoPayload when read last line str = string(line) str = strings.TrimSpace(str) if len(str) == 0 {