diff --git a/.github/workflows/android-branch-auto-sync.yml b/.github/workflows/android-branch-auto-sync.yml deleted file mode 100644 index fd7c9d66..00000000 --- a/.github/workflows/android-branch-auto-sync.yml +++ /dev/null @@ -1,69 +0,0 @@ -name: Android Branch Auto Sync -on: - workflow_dispatch: - push: - paths-ignore: - - "docs/**" - - "README.md" - - ".github/ISSUE_TEMPLATE/**" - branches: - - Alpha - - android-open - tags: - - "v*" - pull_request_target: - branches: - - Alpha - - android-open - -jobs: - update-dependencies: - runs-on: ubuntu-latest - steps: - - name: Checkout Repository - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - - name: Configure Git - run: | - git config --global user.name 'GitHub Action' - git config --global user.email 'action@github.com' - - - name: Sync android-real with Alpha rebase android-open - run: | - git fetch origin - git checkout origin/Alpha -b android-real - git merge --squash origin/android-open - git commit -m "Android: patch" - - - name: Check for conflicts - run: | - CONFLICTS=$(git diff --name-only --diff-filter=U) - if [ ! -z "$CONFLICTS" ]; then - echo "There are conflicts in the following files:" - echo $CONFLICTS - exit 1 - fi - - - name: Push changes - run: | - git push origin android-real --force - - # Send "core-updated" to MetaCubeX/MihomoForAndroid to trigger update-dependencies - trigger-MFA-update: - needs: update-dependencies - runs-on: ubuntu-latest - steps: - - uses: tibdex/github-app-token@v1 - id: generate-token - with: - app_id: ${{ secrets.MAINTAINER_APPID }} - private_key: ${{ secrets.MAINTAINER_APP_PRIVATE_KEY }} - - - name: Trigger update-dependencies - run: | - curl -X POST https://api.github.com/repos/MetaCubeX/MihomoForAndroid/dispatches \ - -H "Accept: application/vnd.github.everest-preview+json" \ - -H "Authorization: token ${{ steps.generate-token.outputs.token }}" \ - -d '{"event_type": "core-updated"}' \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a644f97a..34c80619 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ concurrency: cancel-in-progress: true env: - REGISTRY: docker.io + REGISTRY: ghcr.io jobs: Build: permissions: write-all @@ -161,9 +161,8 @@ jobs: if: ${{ matrix.job.type=='WithCGO' && matrix.job.target=='android' }} id: setup-ndk with: - ndk-version: r26 - add-to-path: false - local-cache: true + ndk-version: r26b + add-to-path: true - name: Build Android if: ${{ matrix.job.type=='WithCGO' && matrix.job.target=='android' }} @@ -343,24 +342,25 @@ jobs: id: meta uses: docker/metadata-action@v4 with: - images: ${{ env.REGISTRY }}/${{ secrets.DOCKERHUB_ACCOUNT }}/${{secrets.DOCKERHUB_REPO}} + images: ${{ env.REGISTRY }}/${{ github.repository }} + - name: Show files run: | ls . ls bin/ - - name: Log into registry - if: github.event_name != 'pull_request' - uses: docker/login-action@v2 + + - name: login to ghcr.io + uses: docker/login-action@v3 with: - registry: ${{ env.REGISTRY }} - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_TOKEN }} + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} # 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 - uses: docker/build-push-action@v4 + uses: docker/build-push-action@v5 with: context: . file: ./Dockerfile @@ -368,8 +368,7 @@ jobs: platforms: | linux/386 linux/amd64 - linux/arm64/v8 + linux/arm64 linux/arm/v7 - # linux/riscv64 tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/trigger-cmfa-update.yml b/.github/workflows/trigger-cmfa-update.yml new file mode 100644 index 00000000..d767657e --- /dev/null +++ b/.github/workflows/trigger-cmfa-update.yml @@ -0,0 +1,33 @@ +name: Trigger CMFA Update +on: + workflow_dispatch: + push: + paths-ignore: + - "docs/**" + - "README.md" + - ".github/ISSUE_TEMPLATE/**" + branches: + - Alpha + tags: + - "v*" + pull_request_target: + branches: + - Alpha + +jobs: + # Send "core-updated" to MetaCubeX/MihomoForAndroid to trigger update-dependencies + trigger-CMFA-update: + runs-on: ubuntu-latest + steps: + - uses: tibdex/github-app-token@v1 + id: generate-token + with: + app_id: ${{ secrets.MAINTAINER_APPID }} + private_key: ${{ secrets.MAINTAINER_APP_PRIVATE_KEY }} + + - name: Trigger update-dependencies + run: | + curl -X POST https://api.github.com/repos/MetaCubeX/MihomoForAndroid/dispatches \ + -H "Accept: application/vnd.github.everest-preview+json" \ + -H "Authorization: token ${{ steps.generate-token.outputs.token }}" \ + -d '{"event_type": "core-updated"}' \ No newline at end of file diff --git a/adapter/adapter.go b/adapter/adapter.go index 74b11bd9..ae9584be 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -17,9 +17,7 @@ import ( "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/dialer" C "github.com/metacubex/mihomo/constant" - "github.com/metacubex/mihomo/log" - - "github.com/puzpuzpuz/xsync/v2" + "github.com/puzpuzpuz/xsync/v3" ) var UnifiedDelay = atomic.NewBool(false) @@ -41,11 +39,6 @@ type Proxy struct { extra *xsync.MapOf[string, *extraProxyState] } -// Alive implements C.Proxy -func (p *Proxy) Alive() bool { - return p.alive.Load() -} - // AliveForTestUrl implements C.Proxy func (p *Proxy) AliveForTestUrl(url string) bool { if state, ok := p.extra.Load(url); ok { @@ -181,7 +174,7 @@ func (p *Proxy) MarshalJSON() ([]byte, error) { _ = json.Unmarshal(inner, &mapping) mapping["history"] = p.DelayHistory() mapping["extra"] = p.ExtraDelayHistory() - mapping["alive"] = p.Alive() + mapping["alive"] = p.AliveForTestUrl(p.url) mapping["name"] = p.Name() mapping["udp"] = p.SupportUDP() mapping["xudp"] = p.SupportXUDP() @@ -191,13 +184,11 @@ func (p *Proxy) MarshalJSON() ([]byte, error) { // URLTest get the delay for the specified URL // implements C.Proxy -func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16], store C.DelayHistoryStoreType) (t uint16, err error) { +func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16]) (t uint16, err error) { defer func() { alive := err == nil - store = p.determineFinalStoreType(store, url) - switch store { - case C.OriginalHistory: + if len(p.url) == 0 || url == p.url { p.alive.Store(alive) record := C.DelayHistory{Time: time.Now()} if alive { @@ -212,7 +203,7 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In if len(p.url) == 0 { p.url = url } - case C.ExtraHistory: + } else { record := C.DelayHistory{Time: time.Now()} if alive { record.Delay = t @@ -236,8 +227,6 @@ func (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.In if state.history.Len() > defaultHistoriesNum { state.history.Pop() } - default: - log.Debugln("health check result will be discarded, url: %s alive: %t, delay: %d", url, alive, t) } }() @@ -316,7 +305,7 @@ func NewProxy(adapter C.ProxyAdapter) *Proxy { history: queue.New[C.DelayHistory](defaultHistoriesNum), alive: atomic.NewBool(true), url: "", - extra: xsync.NewMapOf[*extraProxyState]()} + extra: xsync.NewMapOf[string, *extraProxyState]()} } func urlToMetadata(rawURL string) (addr C.Metadata, err error) { @@ -349,24 +338,3 @@ func urlToMetadata(rawURL string) (addr C.Metadata, err error) { } return } - -func (p *Proxy) determineFinalStoreType(store C.DelayHistoryStoreType, url string) C.DelayHistoryStoreType { - if store != C.DropHistory { - return store - } - - if len(p.url) == 0 || url == p.url { - return C.OriginalHistory - } - - if p.extra.Size() < 2*C.DefaultMaxHealthCheckUrlNum { - return C.ExtraHistory - } - - _, ok := p.extra.Load(url) - if ok { - return C.ExtraHistory - } - - return store -} diff --git a/adapter/inbound/http.go b/adapter/inbound/http.go index 137e17d3..8f912fbe 100644 --- a/adapter/inbound/http.go +++ b/adapter/inbound/http.go @@ -12,6 +12,8 @@ func NewHTTP(target socks5.Addr, srcConn net.Conn, conn net.Conn, additions ...A metadata := parseSocksAddr(target) metadata.NetWork = C.TCP metadata.Type = C.HTTP + metadata.RawSrcAddr = srcConn.RemoteAddr() + metadata.RawDstAddr = srcConn.LocalAddr() ApplyAdditions(metadata, WithSrcAddr(srcConn.RemoteAddr()), WithInAddr(conn.LocalAddr())) ApplyAdditions(metadata, additions...) return conn, metadata diff --git a/adapter/inbound/packet.go b/adapter/inbound/packet.go index 7e245f98..a10d402e 100644 --- a/adapter/inbound/packet.go +++ b/adapter/inbound/packet.go @@ -10,6 +10,8 @@ func NewPacket(target socks5.Addr, packet C.UDPPacket, source C.Type, additions metadata := parseSocksAddr(target) metadata.NetWork = C.UDP metadata.Type = source + metadata.RawSrcAddr = packet.LocalAddr() + metadata.RawDstAddr = metadata.UDPAddr() ApplyAdditions(metadata, WithSrcAddr(packet.LocalAddr())) if p, ok := packet.(C.UDPPacketInAddr); ok { ApplyAdditions(metadata, WithInAddr(p.InAddr())) diff --git a/adapter/outbound/reject.go b/adapter/outbound/reject.go index 5625f932..b564e28d 100644 --- a/adapter/outbound/reject.go +++ b/adapter/outbound/reject.go @@ -13,6 +13,7 @@ import ( type Reject struct { *Base + drop bool } type RejectOption struct { @@ -21,12 +22,18 @@ type RejectOption struct { // DialContext implements C.ProxyAdapter func (r *Reject) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { + if r.drop { + return NewConn(dropConn{}, r), nil + } return NewConn(nopConn{}, r), nil } // ListenPacketContext implements C.ProxyAdapter func (r *Reject) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { - return newPacketConn(nopPacketConn{}, r), nil + if r.drop { + return newPacketConn(&dropPacketConn{}, r), nil + } + return newPacketConn(&nopPacketConn{}, r), nil } func NewRejectWithOption(option RejectOption) *Reject { @@ -50,6 +57,18 @@ func NewReject() *Reject { } } +func NewRejectDrop() *Reject { + return &Reject{ + Base: &Base{ + name: "REJECT-DROP", + tp: C.RejectDrop, + udp: true, + prefer: C.DualStack, + }, + drop: true, + } +} + func NewPass() *Reject { return &Reject{ Base: &Base{ @@ -63,35 +82,29 @@ func NewPass() *Reject { type nopConn struct{} -func (rw nopConn) Read(b []byte) (int, error) { - return 0, io.EOF -} +func (rw nopConn) Read(b []byte) (int, error) { return 0, io.EOF } -func (rw nopConn) ReadBuffer(buffer *buf.Buffer) error { - return io.EOF -} +func (rw nopConn) ReadBuffer(buffer *buf.Buffer) error { return io.EOF } -func (rw nopConn) Write(b []byte) (int, error) { - return 0, io.EOF -} - -func (rw nopConn) WriteBuffer(buffer *buf.Buffer) error { - return io.EOF -} - -func (rw nopConn) Close() error { return nil } -func (rw nopConn) LocalAddr() net.Addr { return nil } -func (rw nopConn) RemoteAddr() net.Addr { return nil } -func (rw nopConn) SetDeadline(time.Time) error { return nil } -func (rw nopConn) SetReadDeadline(time.Time) error { return nil } -func (rw nopConn) SetWriteDeadline(time.Time) error { return nil } +func (rw nopConn) Write(b []byte) (int, error) { return 0, io.EOF } +func (rw nopConn) WriteBuffer(buffer *buf.Buffer) error { return io.EOF } +func (rw nopConn) Close() error { return nil } +func (rw nopConn) LocalAddr() net.Addr { return nil } +func (rw nopConn) RemoteAddr() net.Addr { return nil } +func (rw nopConn) SetDeadline(time.Time) error { return nil } +func (rw nopConn) SetReadDeadline(time.Time) error { return nil } +func (rw nopConn) SetWriteDeadline(time.Time) error { return nil } var udpAddrIPv4Unspecified = &net.UDPAddr{IP: net.IPv4zero, Port: 0} type nopPacketConn struct{} -func (npc nopPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { return len(b), nil } -func (npc nopPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { return 0, nil, io.EOF } +func (npc nopPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { + return len(b), nil +} +func (npc nopPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { + return 0, nil, io.EOF +} func (npc nopPacketConn) WaitReadFrom() ([]byte, func(), net.Addr, error) { return nil, nil, nil, io.EOF } @@ -100,3 +113,38 @@ func (npc nopPacketConn) LocalAddr() net.Addr { return udpAddrIPv4U func (npc nopPacketConn) SetDeadline(time.Time) error { return nil } func (npc nopPacketConn) SetReadDeadline(time.Time) error { return nil } func (npc nopPacketConn) SetWriteDeadline(time.Time) error { return nil } + +type dropConn struct{} + +func (rw dropConn) Read(b []byte) (int, error) { return 0, io.EOF } +func (rw dropConn) ReadBuffer(buffer *buf.Buffer) error { + time.Sleep(C.DefaultDropTime) + return io.EOF +} +func (rw dropConn) Write(b []byte) (int, error) { return 0, io.EOF } +func (rw dropConn) WriteBuffer(buffer *buf.Buffer) error { return io.EOF } +func (rw dropConn) Close() error { return nil } +func (rw dropConn) LocalAddr() net.Addr { return nil } +func (rw dropConn) RemoteAddr() net.Addr { return nil } +func (rw dropConn) SetDeadline(time.Time) error { return nil } +func (rw dropConn) SetReadDeadline(time.Time) error { return nil } +func (rw dropConn) SetWriteDeadline(time.Time) error { return nil } + +type dropPacketConn struct{} + +func (npc dropPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { + time.Sleep(C.DefaultDropTime) + return len(b), nil +} +func (npc dropPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { + time.Sleep(C.DefaultDropTime) + return 0, nil, io.EOF +} +func (npc dropPacketConn) WaitReadFrom() ([]byte, func(), net.Addr, error) { + return nil, nil, nil, io.EOF +} +func (npc dropPacketConn) Close() error { return nil } +func (npc dropPacketConn) LocalAddr() net.Addr { return udpAddrIPv4Unspecified } +func (npc dropPacketConn) SetDeadline(time.Time) error { return nil } +func (npc dropPacketConn) SetReadDeadline(time.Time) error { return nil } +func (npc dropPacketConn) SetWriteDeadline(time.Time) error { return nil } diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index ffc72abb..859a10d6 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -58,15 +58,16 @@ type simpleObfsOption struct { } type v2rayObfsOption struct { - Mode string `obfs:"mode"` - Host string `obfs:"host,omitempty"` - Path string `obfs:"path,omitempty"` - TLS bool `obfs:"tls,omitempty"` - Fingerprint string `obfs:"fingerprint,omitempty"` - Headers map[string]string `obfs:"headers,omitempty"` - SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"` - Mux bool `obfs:"mux,omitempty"` - V2rayHttpUpgrade bool `obfs:"v2ray-http-upgrade,omitempty"` + Mode string `obfs:"mode"` + Host string `obfs:"host,omitempty"` + Path string `obfs:"path,omitempty"` + TLS bool `obfs:"tls,omitempty"` + Fingerprint string `obfs:"fingerprint,omitempty"` + Headers map[string]string `obfs:"headers,omitempty"` + SkipCertVerify bool `obfs:"skip-cert-verify,omitempty"` + Mux bool `obfs:"mux,omitempty"` + V2rayHttpUpgrade bool `obfs:"v2ray-http-upgrade,omitempty"` + V2rayHttpUpgradeFastOpen bool `obfs:"v2ray-http-upgrade-fast-open,omitempty"` } type shadowTLSOption struct { @@ -260,11 +261,12 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) { } obfsMode = opts.Mode v2rayOption = &v2rayObfs.Option{ - Host: opts.Host, - Path: opts.Path, - Headers: opts.Headers, - Mux: opts.Mux, - V2rayHttpUpgrade: opts.V2rayHttpUpgrade, + Host: opts.Host, + Path: opts.Path, + Headers: opts.Headers, + Mux: opts.Mux, + V2rayHttpUpgrade: opts.V2rayHttpUpgrade, + V2rayHttpUpgradeFastOpen: opts.V2rayHttpUpgradeFastOpen, } if opts.TLS { diff --git a/adapter/outbound/singmux.go b/adapter/outbound/singmux.go index dff1e8eb..67267744 100644 --- a/adapter/outbound/singmux.go +++ b/adapter/outbound/singmux.go @@ -10,6 +10,7 @@ import ( "github.com/metacubex/mihomo/component/proxydialer" "github.com/metacubex/mihomo/component/resolver" C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/log" mux "github.com/sagernet/sing-mux" E "github.com/sagernet/sing/common/exceptions" @@ -25,14 +26,21 @@ type SingMux struct { } type SingMuxOption struct { - Enabled bool `proxy:"enabled,omitempty"` - Protocol string `proxy:"protocol,omitempty"` - MaxConnections int `proxy:"max-connections,omitempty"` - MinStreams int `proxy:"min-streams,omitempty"` - MaxStreams int `proxy:"max-streams,omitempty"` - Padding bool `proxy:"padding,omitempty"` - Statistic bool `proxy:"statistic,omitempty"` - OnlyTcp bool `proxy:"only-tcp,omitempty"` + Enabled bool `proxy:"enabled,omitempty"` + Protocol string `proxy:"protocol,omitempty"` + MaxConnections int `proxy:"max-connections,omitempty"` + MinStreams int `proxy:"min-streams,omitempty"` + MaxStreams int `proxy:"max-streams,omitempty"` + Padding bool `proxy:"padding,omitempty"` + Statistic bool `proxy:"statistic,omitempty"` + OnlyTcp bool `proxy:"only-tcp,omitempty"` + BrutalOpts BrutalOption `proxy:"brutal-opts,omitempty"` +} + +type BrutalOption struct { + Enabled bool `proxy:"enabled,omitempty"` + Up string `proxy:"up,omitempty"` + Down string `proxy:"down,omitempty"` } type ProxyBase interface { @@ -94,14 +102,23 @@ func closeSingMux(s *SingMux) { } func NewSingMux(option SingMuxOption, proxy C.ProxyAdapter, base ProxyBase) (C.ProxyAdapter, error) { + // TODO + // "TCP Brutal is only supported on Linux-based systems" + singDialer := proxydialer.NewSingDialer(proxy, dialer.NewDialer(), option.Statistic) client, err := mux.NewClient(mux.Options{ Dialer: singDialer, + Logger: log.SingLogger, Protocol: option.Protocol, MaxConnections: option.MaxConnections, MinStreams: option.MinStreams, MaxStreams: option.MaxStreams, Padding: option.Padding, + Brutal: mux.BrutalOptions{ + Enabled: option.BrutalOpts.Enabled, + SendBPS: StringToBps(option.BrutalOpts.Up), + ReceiveBPS: StringToBps(option.BrutalOpts.Down), + }, }) if err != nil { return nil, err diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index cd1dd28c..b14761a4 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -53,10 +53,12 @@ func (t *Trojan) plainStream(ctx context.Context, c net.Conn) (net.Conn, error) if t.option.Network == "ws" { host, port, _ := net.SplitHostPort(t.addr) wsOpts := &trojan.WebsocketOption{ - Host: host, - Port: port, - Path: t.option.WSOpts.Path, - V2rayHttpUpgrade: t.option.WSOpts.V2rayHttpUpgrade, + Host: host, + Port: port, + Path: t.option.WSOpts.Path, + V2rayHttpUpgrade: t.option.WSOpts.V2rayHttpUpgrade, + V2rayHttpUpgradeFastOpen: t.option.WSOpts.V2rayHttpUpgradeFastOpen, + Headers: http.Header{}, } if t.option.SNI != "" { @@ -64,11 +66,9 @@ func (t *Trojan) plainStream(ctx context.Context, c net.Conn) (net.Conn, error) } if len(t.option.WSOpts.Headers) != 0 { - header := http.Header{} for key, value := range t.option.WSOpts.Headers { - header.Add(key, value) + wsOpts.Headers.Add(key, value) } - wsOpts.Headers = header } return t.instance.StreamWebsocketConn(ctx, c, wsOpts) diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index dbe8b1a4..ceeb52a5 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -88,14 +88,15 @@ func (v *Vless) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M case "ws": host, port, _ := net.SplitHostPort(v.addr) wsOpts := &vmess.WebsocketConfig{ - Host: host, - Port: port, - Path: v.option.WSOpts.Path, - MaxEarlyData: v.option.WSOpts.MaxEarlyData, - EarlyDataHeaderName: v.option.WSOpts.EarlyDataHeaderName, - V2rayHttpUpgrade: v.option.WSOpts.V2rayHttpUpgrade, - ClientFingerprint: v.option.ClientFingerprint, - Headers: http.Header{}, + Host: host, + Port: port, + Path: v.option.WSOpts.Path, + MaxEarlyData: v.option.WSOpts.MaxEarlyData, + EarlyDataHeaderName: v.option.WSOpts.EarlyDataHeaderName, + V2rayHttpUpgrade: v.option.WSOpts.V2rayHttpUpgrade, + V2rayHttpUpgradeFastOpen: v.option.WSOpts.V2rayHttpUpgradeFastOpen, + ClientFingerprint: v.option.ClientFingerprint, + Headers: http.Header{}, } if len(v.option.WSOpts.Headers) != 0 { diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index 8811fb0d..c1c981ce 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -87,11 +87,12 @@ type GrpcOptions struct { } type WSOptions struct { - Path string `proxy:"path,omitempty"` - Headers map[string]string `proxy:"headers,omitempty"` - MaxEarlyData int `proxy:"max-early-data,omitempty"` - EarlyDataHeaderName string `proxy:"early-data-header-name,omitempty"` - V2rayHttpUpgrade bool `proxy:"v2ray-http-upgrade,omitempty"` + Path string `proxy:"path,omitempty"` + Headers map[string]string `proxy:"headers,omitempty"` + MaxEarlyData int `proxy:"max-early-data,omitempty"` + EarlyDataHeaderName string `proxy:"early-data-header-name,omitempty"` + V2rayHttpUpgrade bool `proxy:"v2ray-http-upgrade,omitempty"` + V2rayHttpUpgradeFastOpen bool `proxy:"v2ray-http-upgrade-fast-open,omitempty"` } // StreamConnContext implements C.ProxyAdapter @@ -106,14 +107,15 @@ func (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.M case "ws": host, port, _ := net.SplitHostPort(v.addr) wsOpts := &mihomoVMess.WebsocketConfig{ - Host: host, - Port: port, - Path: v.option.WSOpts.Path, - MaxEarlyData: v.option.WSOpts.MaxEarlyData, - EarlyDataHeaderName: v.option.WSOpts.EarlyDataHeaderName, - V2rayHttpUpgrade: v.option.WSOpts.V2rayHttpUpgrade, - ClientFingerprint: v.option.ClientFingerprint, - Headers: http.Header{}, + Host: host, + Port: port, + Path: v.option.WSOpts.Path, + MaxEarlyData: v.option.WSOpts.MaxEarlyData, + EarlyDataHeaderName: v.option.WSOpts.EarlyDataHeaderName, + V2rayHttpUpgrade: v.option.WSOpts.V2rayHttpUpgrade, + V2rayHttpUpgradeFastOpen: v.option.WSOpts.V2rayHttpUpgradeFastOpen, + ClientFingerprint: v.option.ClientFingerprint, + Headers: http.Header{}, } if len(v.option.WSOpts.Headers) != 0 { diff --git a/adapter/outboundgroup/fallback.go b/adapter/outboundgroup/fallback.go index d0dd98b1..50427e53 100644 --- a/adapter/outboundgroup/fallback.go +++ b/adapter/outboundgroup/fallback.go @@ -84,11 +84,11 @@ func (f *Fallback) MarshalJSON() ([]byte, error) { all = append(all, proxy.Name()) } return json.Marshal(map[string]any{ - "type": f.Type().String(), - "now": f.Now(), - "all": all, - "testUrl": f.testUrl, - "expected": f.expectedStatus, + "type": f.Type().String(), + "now": f.Now(), + "all": all, + "testUrl": f.testUrl, + "expectedStatus": f.expectedStatus, }) } @@ -102,13 +102,11 @@ func (f *Fallback) findAliveProxy(touch bool) C.Proxy { proxies := f.GetProxies(touch) for _, proxy := range proxies { if len(f.selected) == 0 { - // if proxy.Alive() { if proxy.AliveForTestUrl(f.testUrl) { return proxy } } else { if proxy.Name() == f.selected { - // if proxy.Alive() { if proxy.AliveForTestUrl(f.testUrl) { return proxy } else { @@ -135,12 +133,11 @@ func (f *Fallback) Set(name string) error { } f.selected = name - // if !p.Alive() { if !p.AliveForTestUrl(f.testUrl) { ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(5000)) defer cancel() expectedStatus, _ := utils.NewIntRanges[uint16](f.expectedStatus) - _, _ = p.URLTest(ctx, f.testUrl, expectedStatus, C.ExtraHistory) + _, _ = p.URLTest(ctx, f.testUrl, expectedStatus) } return nil diff --git a/adapter/outboundgroup/groupbase.go b/adapter/outboundgroup/groupbase.go index d4a812f6..0ea3685b 100644 --- a/adapter/outboundgroup/groupbase.go +++ b/adapter/outboundgroup/groupbase.go @@ -202,7 +202,7 @@ func (gb *GroupBase) URLTest(ctx context.Context, url string, expectedStatus uti proxy := proxy wg.Add(1) go func() { - delay, err := proxy.URLTest(ctx, url, expectedStatus, C.DropHistory) + delay, err := proxy.URLTest(ctx, url, expectedStatus) if err == nil { lock.Lock() mp[proxy.Name()] = delay @@ -222,7 +222,7 @@ func (gb *GroupBase) URLTest(ctx context.Context, url string, expectedStatus uti } func (gb *GroupBase) onDialFailed(adapterType C.AdapterType, err error) { - if adapterType == C.Direct || adapterType == C.Compatible || adapterType == C.Reject || adapterType == C.Pass { + if adapterType == C.Direct || adapterType == C.Compatible || adapterType == C.Reject || adapterType == C.Pass || adapterType == C.RejectDrop { return } diff --git a/adapter/outboundgroup/loadbalance.go b/adapter/outboundgroup/loadbalance.go index 885aeaee..7fbc91a7 100644 --- a/adapter/outboundgroup/loadbalance.go +++ b/adapter/outboundgroup/loadbalance.go @@ -10,8 +10,8 @@ import ( "time" "github.com/metacubex/mihomo/adapter/outbound" - "github.com/metacubex/mihomo/common/cache" "github.com/metacubex/mihomo/common/callback" + "github.com/metacubex/mihomo/common/lru" N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/dialer" @@ -190,9 +190,9 @@ func strategyConsistentHashing(url string) strategyFn { func strategyStickySessions(url string) strategyFn { ttl := time.Minute * 10 maxRetry := 5 - lruCache := cache.New[uint64, int]( - cache.WithAge[uint64, int](int64(ttl.Seconds())), - cache.WithSize[uint64, int](1000)) + lruCache := lru.New[uint64, int]( + lru.WithAge[uint64, int](int64(ttl.Seconds())), + lru.WithSize[uint64, int](1000)) return func(proxies []C.Proxy, metadata *C.Metadata, touch bool) C.Proxy { key := utils.MapHash(getKeyWithSrcAndDst(metadata)) length := len(proxies) diff --git a/adapter/outboundgroup/parser.go b/adapter/outboundgroup/parser.go index 8f3335d8..422349fe 100644 --- a/adapter/outboundgroup/parser.go +++ b/adapter/outboundgroup/parser.go @@ -22,18 +22,19 @@ var ( type GroupCommonOption struct { outbound.BasicOption - Name string `group:"name"` - Type string `group:"type"` - Proxies []string `group:"proxies,omitempty"` - Use []string `group:"use,omitempty"` - URL string `group:"url,omitempty"` - Interval int `group:"interval,omitempty"` - Lazy bool `group:"lazy,omitempty"` - DisableUDP bool `group:"disable-udp,omitempty"` - Filter string `group:"filter,omitempty"` - ExcludeFilter string `group:"exclude-filter,omitempty"` - ExcludeType string `group:"exclude-type,omitempty"` - ExpectedStatus string `group:"expected-status,omitempty"` + Name string `group:"name"` + Type string `group:"type"` + Proxies []string `group:"proxies,omitempty"` + Use []string `group:"use,omitempty"` + URL string `group:"url,omitempty"` + Interval int `group:"interval,omitempty"` + Lazy bool `group:"lazy,omitempty"` + DisableUDP bool `group:"disable-udp,omitempty"` + Filter string `group:"filter,omitempty"` + ExcludeFilter string `group:"exclude-filter,omitempty"` + ExcludeType string `group:"exclude-type,omitempty"` + ExpectedStatus string `group:"expected-status,omitempty"` + IncludeAllProviders bool `group:"include-all-providers,omitempty"` } func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, providersMap map[string]types.ProxyProvider) (C.ProxyAdapter, error) { @@ -54,7 +55,18 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide providers := []types.ProxyProvider{} - if len(groupOption.Proxies) == 0 && len(groupOption.Use) == 0 { + var GroupUse []string + visited := make(map[string]bool) + if groupOption.IncludeAllProviders { + for name := range provider.ProxyProviderName { + GroupUse = append(GroupUse, name) + visited[name] = true + } + } else { + GroupUse = groupOption.Use + } + + if len(groupOption.Proxies) == 0 && len(GroupUse) == 0 { return nil, fmt.Errorf("%s: %w", groupName, errMissProxy) } @@ -80,24 +92,20 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide return nil, fmt.Errorf("%s: %w", groupName, errDuplicateProvider) } - var url string - var interval uint - // select don't need health check if groupOption.Type != "select" && groupOption.Type != "relay" { if groupOption.URL == "" { - groupOption.URL = "https://cp.cloudflare.com/generate_204" + groupOption.URL = C.DefaultTestURL + testUrl = groupOption.URL } if groupOption.Interval == 0 { groupOption.Interval = 300 } - - url = groupOption.URL - interval = uint(groupOption.Interval) } - hc := provider.NewHealthCheck(ps, url, interval, true, expectedStatus) + hc := provider.NewHealthCheck(ps, groupOption.URL, uint(groupOption.Interval), true, expectedStatus) + pd, err := provider.NewCompatibleProvider(groupName, ps, hc) if err != nil { return nil, fmt.Errorf("%s: %w", groupName, err) @@ -107,8 +115,8 @@ func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, provide providersMap[groupName] = pd } - if len(groupOption.Use) != 0 { - list, err := getProviders(providersMap, groupOption.Use) + if len(GroupUse) != 0 { + list, err := getProviders(providersMap, GroupUse) if err != nil { return nil, fmt.Errorf("%s: %w", groupName, err) } diff --git a/adapter/outboundgroup/patch_android.go b/adapter/outboundgroup/patch_android.go new file mode 100644 index 00000000..c6566814 --- /dev/null +++ b/adapter/outboundgroup/patch_android.go @@ -0,0 +1,64 @@ +//go:build android && cmfa + +package outboundgroup + +import ( + C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/constant/provider" +) + +type ProxyGroup interface { + C.ProxyAdapter + + Providers() []provider.ProxyProvider + Proxies() []C.Proxy + Now() string +} + +func (f *Fallback) Providers() []provider.ProxyProvider { + return f.providers +} + +func (lb *LoadBalance) Providers() []provider.ProxyProvider { + return lb.providers +} + +func (f *Fallback) Proxies() []C.Proxy { + return f.GetProxies(false) +} + +func (lb *LoadBalance) Proxies() []C.Proxy { + return lb.GetProxies(false) +} + +func (lb *LoadBalance) Now() string { + return "" +} + +func (r *Relay) Providers() []provider.ProxyProvider { + return r.providers +} + +func (r *Relay) Proxies() []C.Proxy { + return r.GetProxies(false) +} + +func (r *Relay) Now() string { + return "" +} + +func (s *Selector) Providers() []provider.ProxyProvider { + return s.providers +} + +func (s *Selector) Proxies() []C.Proxy { + return s.GetProxies(false) +} + +func (u *URLTest) Providers() []provider.ProxyProvider { + return u.providers +} + +func (u *URLTest) Proxies() []C.Proxy { + return u.GetProxies(false) +} diff --git a/adapter/outboundgroup/urltest.go b/adapter/outboundgroup/urltest.go index 8c861768..bdac909f 100644 --- a/adapter/outboundgroup/urltest.go +++ b/adapter/outboundgroup/urltest.go @@ -101,7 +101,7 @@ func (u *URLTest) fast(touch bool) C.Proxy { proxies := u.GetProxies(touch) if u.selected != "" { for _, proxy := range proxies { - if !proxy.Alive() { + if !proxy.AliveForTestUrl(u.testUrl) { continue } if proxy.Name() == u.selected { @@ -113,7 +113,6 @@ func (u *URLTest) fast(touch bool) C.Proxy { elm, _, shared := u.fastSingle.Do(func() (C.Proxy, error) { fast := proxies[0] - // min := fast.LastDelay() min := fast.LastDelayForTestUrl(u.testUrl) fastNotExist := true @@ -122,12 +121,10 @@ func (u *URLTest) fast(touch bool) C.Proxy { fastNotExist = false } - // if !proxy.Alive() { if !proxy.AliveForTestUrl(u.testUrl) { continue } - // delay := proxy.LastDelay() delay := proxy.LastDelayForTestUrl(u.testUrl) if delay < min { fast = proxy @@ -136,7 +133,6 @@ func (u *URLTest) fast(touch bool) C.Proxy { } // tolerance - // if u.fastNode == nil || fastNotExist || !u.fastNode.Alive() || u.fastNode.LastDelay() > fast.LastDelay()+u.tolerance { if u.fastNode == nil || fastNotExist || !u.fastNode.AliveForTestUrl(u.testUrl) || u.fastNode.LastDelayForTestUrl(u.testUrl) > fast.LastDelayForTestUrl(u.testUrl)+u.tolerance { u.fastNode = fast } @@ -169,11 +165,11 @@ func (u *URLTest) MarshalJSON() ([]byte, error) { all = append(all, proxy.Name()) } return json.Marshal(map[string]any{ - "type": u.Type().String(), - "now": u.Now(), - "all": all, - "testUrl": u.testUrl, - "expected": u.expectedStatus, + "type": u.Type().String(), + "now": u.Now(), + "all": all, + "testUrl": u.testUrl, + "expectedStatus": u.expectedStatus, }) } diff --git a/adapter/provider/healthcheck.go b/adapter/provider/healthcheck.go index e7f021e1..d8e56192 100644 --- a/adapter/provider/healthcheck.go +++ b/adapter/provider/healthcheck.go @@ -104,12 +104,6 @@ func (hc *HealthCheck) registerHealthCheckTask(url string, expectedStatus utils. return } - // due to the time-consuming nature of health checks, a maximum of defaultMaxTestURLNum URLs can be set for testing - if len(hc.extra) > C.DefaultMaxHealthCheckUrlNum { - log.Debugln("skip add url: %s to health check because it has reached the maximum limit: %d", url, C.DefaultMaxHealthCheckUrlNum) - return - } - option := &extraOption{filters: map[string]struct{}{}, expectedStatus: expectedStatus} splitAndAddFiltersToExtra(filter, option) hc.extra[url] = option @@ -148,6 +142,10 @@ func (hc *HealthCheck) stop() { } func (hc *HealthCheck) check() { + if len(hc.proxies) == 0 { + return + } + _, _, _ = hc.singleDo.Do(func() (struct{}, error) { id := utils.NewUUIDV4().String() log.Debugln("Start New Health Checking {%s}", id) @@ -177,13 +175,8 @@ func (hc *HealthCheck) execute(b *batch.Batch[bool], url, uid string, option *ex } var filterReg *regexp2.Regexp - var store = C.OriginalHistory var expectedStatus utils.IntRanges[uint16] if option != nil { - if url != hc.url { - store = C.ExtraHistory - } - expectedStatus = option.expectedStatus if len(option.filters) != 0 { filters := make([]string, 0, len(option.filters)) @@ -208,7 +201,7 @@ func (hc *HealthCheck) execute(b *batch.Batch[bool], url, uid string, option *ex ctx, cancel := context.WithTimeout(context.Background(), defaultURLTestTimeout) defer cancel() log.Debugln("Health Checking, proxy: %s, url: %s, id: {%s}", p.Name(), url, uid) - _, _ = p.URLTest(ctx, url, expectedStatus, store) + _, _ = p.URLTest(ctx, url, expectedStatus) log.Debugln("Health Checked, proxy: %s, url: %s, alive: %t, delay: %d ms uid: {%s}", p.Name(), url, p.AliveForTestUrl(url), p.LastDelayForTestUrl(url), uid) return false, nil }) @@ -223,6 +216,7 @@ func NewHealthCheck(proxies []C.Proxy, url string, interval uint, lazy bool, exp if len(url) == 0 { interval = 0 expectedStatus = nil + url = C.DefaultTestURL } return &HealthCheck{ diff --git a/adapter/provider/parser.go b/adapter/provider/parser.go index 321380ed..a22492d2 100644 --- a/adapter/provider/parser.go +++ b/adapter/provider/parser.go @@ -9,6 +9,7 @@ import ( "github.com/metacubex/mihomo/common/utils" "github.com/metacubex/mihomo/component/resource" C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/constant/features" types "github.com/metacubex/mihomo/constant/provider" ) @@ -25,16 +26,26 @@ type healthCheckSchema struct { ExpectedStatus string `provider:"expected-status,omitempty"` } +type OverrideSchema struct { + UDP *bool `provider:"udp,omitempty"` + Up *string `provider:"up,omitempty"` + Down *string `provider:"down,omitempty"` + DialerProxy *string `provider:"dialer-proxy,omitempty"` + SkipCertVerify *bool `provider:"skip-cert-verify,omitempty"` +} + type proxyProviderSchema struct { - Type string `provider:"type"` - Path string `provider:"path,omitempty"` - URL string `provider:"url,omitempty"` - Interval int `provider:"interval,omitempty"` - Filter string `provider:"filter,omitempty"` - ExcludeFilter string `provider:"exclude-filter,omitempty"` - ExcludeType string `provider:"exclude-type,omitempty"` - DialerProxy string `provider:"dialer-proxy,omitempty"` - HealthCheck healthCheckSchema `provider:"health-check,omitempty"` + Type string `provider:"type"` + Path string `provider:"path,omitempty"` + URL string `provider:"url,omitempty"` + Interval int `provider:"interval,omitempty"` + Filter string `provider:"filter,omitempty"` + ExcludeFilter string `provider:"exclude-filter,omitempty"` + ExcludeType string `provider:"exclude-type,omitempty"` + DialerProxy string `provider:"dialer-proxy,omitempty"` + + HealthCheck healthCheckSchema `provider:"health-check,omitempty"` + Override OverrideSchema `provider:"override,omitempty"` } func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvider, error) { @@ -68,7 +79,7 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide case "http": if schema.Path != "" { path := C.Path.Resolve(schema.Path) - if !C.Path.IsSafePath(path) { + if !features.CMFA && !C.Path.IsSafePath(path) { return nil, fmt.Errorf("%w: %s", errSubPath, path) } vehicle = resource.NewHTTPVehicle(schema.URL, path) @@ -85,6 +96,7 @@ func ParseProxyProvider(name string, mapping map[string]any) (types.ProxyProvide excludeFilter := schema.ExcludeFilter excludeType := schema.ExcludeType dialerProxy := schema.DialerProxy + override := schema.Override - return NewProxySetProvider(name, interval, filter, excludeFilter, excludeType, dialerProxy, vehicle, hc) + return NewProxySetProvider(name, interval, filter, excludeFilter, excludeType, dialerProxy, override, vehicle, hc) } diff --git a/adapter/provider/patch_android.go b/adapter/provider/patch_android.go new file mode 100644 index 00000000..e9042bda --- /dev/null +++ b/adapter/provider/patch_android.go @@ -0,0 +1,36 @@ +//go:build android && cmfa + +package provider + +import ( + "time" +) + +var ( + suspended bool +) + +type UpdatableProvider interface { + UpdatedAt() time.Time +} + +func (pp *proxySetProvider) UpdatedAt() time.Time { + return pp.Fetcher.UpdatedAt +} + +func (pp *proxySetProvider) Close() error { + pp.healthCheck.close() + pp.Fetcher.Destroy() + + return nil +} + +func (cp *compatibleProvider) Close() error { + cp.healthCheck.close() + + return nil +} + +func Suspend(s bool) { + suspended = s +} diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index ffaec91b..01ae44ee 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -24,6 +24,8 @@ import ( "gopkg.in/yaml.v3" ) +var ProxyProviderName = make(map[string]struct{}) + const ( ReservedName = "default" ) @@ -46,12 +48,18 @@ type proxySetProvider struct { } func (pp *proxySetProvider) MarshalJSON() ([]byte, error) { + expectedStatus := "*" + if pp.healthCheck.expectedStatus != nil { + expectedStatus = pp.healthCheck.expectedStatus.ToString() + } + return json.Marshal(map[string]any{ "name": pp.Name(), "type": pp.Type().String(), "vehicleType": pp.VehicleType().String(), "proxies": pp.Proxies(), "testUrl": pp.healthCheck.url, + "expectedStatus": expectedStatus, "updatedAt": pp.UpdatedAt, "subscriptionInfo": pp.subscriptionInfo, }) @@ -120,7 +128,7 @@ func (pp *proxySetProvider) getSubscriptionInfo() { ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() resp, err := mihomoHttp.HttpRequest(ctx, pp.Vehicle().(*resource.HTTPVehicle).Url(), - http.MethodGet, http.Header{"User-Agent": {"mihomo"}}, nil) + http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) if err != nil { return } @@ -163,7 +171,7 @@ func stopProxyProvider(pd *ProxySetProvider) { _ = pd.Fetcher.Destroy() } -func NewProxySetProvider(name string, interval time.Duration, filter string, excludeFilter string, excludeType string, dialerProxy string, vehicle types.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) { +func NewProxySetProvider(name string, interval time.Duration, filter string, excludeFilter string, excludeType string, dialerProxy string, override OverrideSchema, vehicle types.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) { excludeFilterReg, err := regexp2.Compile(excludeFilter, 0) if err != nil { return nil, fmt.Errorf("invalid excludeFilter regex: %w", err) @@ -191,8 +199,9 @@ func NewProxySetProvider(name string, interval time.Duration, filter string, exc healthCheck: hc, } - fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, excludeFilter, excludeTypeArray, filterRegs, excludeFilterReg, dialerProxy), proxiesOnUpdate(pd)) + fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, excludeFilter, excludeTypeArray, filterRegs, excludeFilterReg, dialerProxy, override), proxiesOnUpdate(pd)) pd.Fetcher = fetcher + ProxyProviderName[name] = struct{}{} wrapper := &ProxySetProvider{pd} runtime.SetFinalizer(wrapper, stopProxyProvider) return wrapper, nil @@ -211,12 +220,18 @@ type compatibleProvider struct { } func (cp *compatibleProvider) MarshalJSON() ([]byte, error) { + expectedStatus := "*" + if cp.healthCheck.expectedStatus != nil { + expectedStatus = cp.healthCheck.expectedStatus.ToString() + } + return json.Marshal(map[string]any{ - "name": cp.Name(), - "type": cp.Type().String(), - "vehicleType": cp.VehicleType().String(), - "proxies": cp.Proxies(), - "testUrl": cp.healthCheck.url, + "name": cp.Name(), + "type": cp.Type().String(), + "vehicleType": cp.VehicleType().String(), + "proxies": cp.Proxies(), + "testUrl": cp.healthCheck.url, + "expectedStatus": expectedStatus, }) } @@ -292,7 +307,7 @@ func proxiesOnUpdate(pd *proxySetProvider) func([]C.Proxy) { } } -func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray []string, filterRegs []*regexp2.Regexp, excludeFilterReg *regexp2.Regexp, dialerProxy string) resource.Parser[[]C.Proxy] { +func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray []string, filterRegs []*regexp2.Regexp, excludeFilterReg *regexp2.Regexp, dialerProxy string, override OverrideSchema) resource.Parser[[]C.Proxy] { return func(buf []byte) ([]C.Proxy, error) { schema := &ProxySchema{} @@ -355,13 +370,32 @@ func proxiesParseAndFilter(filter string, excludeFilter string, excludeTypeArray if _, ok := proxiesSet[name]; ok { continue } + if len(dialerProxy) > 0 { 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 + } + proxy, err := adapter.ParseProxy(mapping) if err != nil { return nil, fmt.Errorf("proxy %d error: %w", idx, err) } + proxiesSet[name] = struct{}{} proxies = append(proxies, proxy) } diff --git a/adapter/provider/subscription_info.go b/adapter/provider/subscription_info.go index 8b90601c..3a9e2d72 100644 --- a/adapter/provider/subscription_info.go +++ b/adapter/provider/subscription_info.go @@ -25,7 +25,11 @@ func NewSubscriptionInfo(userinfo string) (si *SubscriptionInfo, err error) { case "total": si.Total, err = strconv.ParseInt(value, 10, 64) case "expire": - si.Expire, err = strconv.ParseInt(value, 10, 64) + if value == "" { + si.Expire = 0 + } else { + si.Expire, err = strconv.ParseInt(value, 10, 64) + } } if err != nil { return diff --git a/common/arc/arc.go b/common/arc/arc.go new file mode 100644 index 00000000..8e62c906 --- /dev/null +++ b/common/arc/arc.go @@ -0,0 +1,229 @@ +package arc + +import ( + "sync" + "time" + + list "github.com/bahlo/generic-list-go" + "github.com/samber/lo" +) + +//modify from https://github.com/alexanderGugel/arc + +// Option is part of Functional Options Pattern +type Option[K comparable, V any] func(*ARC[K, V]) + +func WithSize[K comparable, V any](maxSize int) Option[K, V] { + return func(a *ARC[K, V]) { + a.c = maxSize + } +} + +type ARC[K comparable, V any] struct { + p int + c int + t1 *list.List[*entry[K, V]] + b1 *list.List[*entry[K, V]] + t2 *list.List[*entry[K, V]] + b2 *list.List[*entry[K, V]] + mutex sync.Mutex + len int + cache map[K]*entry[K, V] + staleReturn bool +} + +// New returns a new Adaptive Replacement Cache (ARC). +func New[K comparable, V any](options ...Option[K, V]) *ARC[K, V] { + arc := &ARC[K, V]{ + p: 0, + t1: list.New[*entry[K, V]](), + b1: list.New[*entry[K, V]](), + t2: list.New[*entry[K, V]](), + b2: list.New[*entry[K, V]](), + len: 0, + cache: make(map[K]*entry[K, V]), + } + + for _, option := range options { + option(arc) + } + return arc +} + +// Set inserts a new key-value pair into the cache. +// This optimizes future access to this entry (side effect). +func (a *ARC[K, V]) Set(key K, value V) { + a.mutex.Lock() + defer a.mutex.Unlock() + + a.set(key, value) +} + +func (a *ARC[K, V]) set(key K, value V) { + a.setWithExpire(key, value, time.Unix(0, 0)) +} + +// SetWithExpire stores any representation of a response for a given key and given expires. +// The expires time will round to second. +func (a *ARC[K, V]) SetWithExpire(key K, value V, expires time.Time) { + a.mutex.Lock() + defer a.mutex.Unlock() + + a.setWithExpire(key, value, expires) +} + +func (a *ARC[K, V]) setWithExpire(key K, value V, expires time.Time) { + ent, ok := a.cache[key] + if ok != true { + a.len++ + ent := &entry[K, V]{key: key, value: value, ghost: false, expires: expires.Unix()} + a.req(ent) + a.cache[key] = ent + } else { + if ent.ghost { + a.len++ + } + ent.value = value + ent.ghost = false + ent.expires = expires.Unix() + a.req(ent) + } +} + +// Get retrieves a previously via Set inserted entry. +// This optimizes future access to this entry (side effect). +func (a *ARC[K, V]) Get(key K) (value V, ok bool) { + a.mutex.Lock() + defer a.mutex.Unlock() + + ent, ok := a.get(key) + if ok { + return ent.value, true + } + return lo.Empty[V](), false +} + +func (a *ARC[K, V]) get(key K) (e *entry[K, V], ok bool) { + ent, ok := a.cache[key] + if ok { + a.req(ent) + return ent, !ent.ghost + } + return ent, false +} + +// GetWithExpire returns any representation of a cached response, +// a time.Time Give expected expires, +// and a bool set to true if the key was found. +// This method will NOT check the maxAge of element and will NOT update the expires. +func (a *ARC[K, V]) GetWithExpire(key K) (V, time.Time, bool) { + a.mutex.Lock() + defer a.mutex.Unlock() + + ent, ok := a.get(key) + if !ok { + return lo.Empty[V](), time.Time{}, false + } + + return ent.value, time.Unix(ent.expires, 0), true +} + +// Len determines the number of currently cached entries. +// This method is side-effect free in the sense that it does not attempt to optimize random cache access. +func (a *ARC[K, V]) Len() int { + a.mutex.Lock() + defer a.mutex.Unlock() + + return a.len +} + +func (a *ARC[K, V]) req(ent *entry[K, V]) { + if ent.ll == a.t1 || ent.ll == a.t2 { + // Case I + ent.setMRU(a.t2) + } else if ent.ll == a.b1 { + // Case II + // Cache Miss in t1 and t2 + + // Adaptation + var d int + if a.b1.Len() >= a.b2.Len() { + d = 1 + } else { + d = a.b2.Len() / a.b1.Len() + } + + // a.p = min(a.p+d, a.c) + a.p = a.p + d + if a.c < a.p { + a.p = a.c + } + + a.replace(ent) + ent.setMRU(a.t2) + } else if ent.ll == a.b2 { + // Case III + // Cache Miss in t1 and t2 + + // Adaptation + var d int + if a.b2.Len() >= a.b1.Len() { + d = 1 + } else { + d = a.b1.Len() / a.b2.Len() + } + //a.p = max(a.p-d, 0) + a.p = a.p - d + if a.p < 0 { + a.p = 0 + } + + a.replace(ent) + ent.setMRU(a.t2) + } else if ent.ll == nil { + // Case IV + + if a.t1.Len()+a.b1.Len() == a.c { + // Case A + if a.t1.Len() < a.c { + a.delLRU(a.b1) + a.replace(ent) + } else { + a.delLRU(a.t1) + } + } else if a.t1.Len()+a.b1.Len() < a.c { + // Case B + if a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() >= a.c { + if a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() == 2*a.c { + a.delLRU(a.b2) + } + a.replace(ent) + } + } + + ent.setMRU(a.t1) + } +} + +func (a *ARC[K, V]) delLRU(list *list.List[*entry[K, V]]) { + lru := list.Back() + list.Remove(lru) + a.len-- + delete(a.cache, lru.Value.key) +} + +func (a *ARC[K, V]) replace(ent *entry[K, V]) { + if a.t1.Len() > 0 && ((a.t1.Len() > a.p) || (ent.ll == a.b2 && a.t1.Len() == a.p)) { + lru := a.t1.Back().Value + lru.value = lo.Empty[V]() + lru.ghost = true + a.len-- + lru.setMRU(a.b1) + } else { + lru := a.t2.Back().Value + lru.value = lo.Empty[V]() + lru.ghost = true + a.len-- + lru.setMRU(a.b2) + } +} diff --git a/common/arc/arc_test.go b/common/arc/arc_test.go new file mode 100644 index 00000000..cb9835a3 --- /dev/null +++ b/common/arc/arc_test.go @@ -0,0 +1,59 @@ +package arc + +import "testing" + +func TestBasic(t *testing.T) { + cache := New[string, string](WithSize[string, string](3)) + if cache.Len() != 0 { + t.Error("Empty cache should have length 0") + } + + cache.Set("Hello", "World") + if cache.Len() != 1 { + t.Error("Cache should have length 1") + } + + var val interface{} + var ok bool + + if val, ok = cache.Get("Hello"); val != "World" || ok != true { + t.Error("Didn't set \"Hello\" to \"World\"") + } + + cache.Set("Hello", "World1") + if cache.Len() != 1 { + t.Error("Inserting the same entry multiple times shouldn't increase cache size") + } + + if val, ok = cache.Get("Hello"); val != "World1" || ok != true { + t.Error("Didn't update \"Hello\" to \"World1\"") + } + + cache.Set("Hallo", "Welt") + if cache.Len() != 2 { + t.Error("Inserting two different entries should result into lenght=2") + } + + if val, ok = cache.Get("Hallo"); val != "Welt" || ok != true { + t.Error("Didn't set \"Hallo\" to \"Welt\"") + } +} + +func TestBasicReplace(t *testing.T) { + cache := New[string, string](WithSize[string, string](3)) + + cache.Set("Hello", "Hallo") + cache.Set("World", "Welt") + cache.Get("World") + cache.Set("Cache", "Cache") + cache.Set("Replace", "Ersetzen") + + value, ok := cache.Get("World") + if !ok || value != "Welt" { + t.Error("ARC should have replaced \"Hello\"") + } + + if cache.Len() != 3 { + t.Error("ARC should have a maximum size of 3") + } +} diff --git a/common/arc/entry.go b/common/arc/entry.go new file mode 100644 index 00000000..679cbc1c --- /dev/null +++ b/common/arc/entry.go @@ -0,0 +1,32 @@ +package arc + +import ( + list "github.com/bahlo/generic-list-go" +) + +type entry[K comparable, V any] struct { + key K + value V + ll *list.List[*entry[K, V]] + el *list.Element[*entry[K, V]] + ghost bool + expires int64 +} + +func (e *entry[K, V]) setLRU(list *list.List[*entry[K, V]]) { + e.detach() + e.ll = list + e.el = e.ll.PushBack(e) +} + +func (e *entry[K, V]) setMRU(list *list.List[*entry[K, V]]) { + e.detach() + e.ll = list + e.el = e.ll.PushFront(e) +} + +func (e *entry[K, V]) detach() { + if e.ll != nil { + e.ll.Remove(e.el) + } +} diff --git a/common/atomic/value.go b/common/atomic/value.go index 708fcf90..cbc6c5b8 100644 --- a/common/atomic/value.go +++ b/common/atomic/value.go @@ -11,8 +11,8 @@ func DefaultValue[T any]() T { } type TypedValue[T any] struct { - value atomic.Value _ noCopy + value atomic.Value } func (t *TypedValue[T]) Load() T { diff --git a/common/collections/stack.go b/common/collections/stack.go deleted file mode 100644 index 74673f9a..00000000 --- a/common/collections/stack.go +++ /dev/null @@ -1,56 +0,0 @@ -package collections - -import "sync" - -type ( - stack struct { - top *node - length int - lock *sync.RWMutex - } - - node struct { - value interface{} - prev *node - } -) - -// NewStack Create a new stack -func NewStack() *stack { - return &stack{nil, 0, &sync.RWMutex{}} -} - -// Len Return the number of items in the stack -func (this *stack) Len() int { - return this.length -} - -// Peek View the top item on the stack -func (this *stack) Peek() interface{} { - if this.length == 0 { - return nil - } - return this.top.value -} - -// Pop the top item of the stack and return it -func (this *stack) Pop() interface{} { - this.lock.Lock() - defer this.lock.Unlock() - if this.length == 0 { - return nil - } - n := this.top - this.top = n.prev - this.length-- - return n.value -} - -// Push a value onto the top of the stack -func (this *stack) Push(value interface{}) { - this.lock.Lock() - defer this.lock.Unlock() - n := &node{value, this.top} - this.top = n - this.length++ -} diff --git a/common/convert/converter.go b/common/convert/converter.go index 55035bbe..bf5bcbd7 100644 --- a/common/convert/converter.go +++ b/common/convert/converter.go @@ -142,6 +142,8 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { tuic["udp-relay-mode"] = udpRelayMode } + proxies = append(proxies, tuic) + case "trojan": urlTrojan, err := url.Parse(line) if err != nil { diff --git a/common/generics/list/list.go b/common/generics/list/list.go deleted file mode 100644 index 04d84180..00000000 --- a/common/generics/list/list.go +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package list implements a doubly linked list. -// -// To iterate over a list (where l is a *List): -// -// for e := l.Front(); e != nil; e = e.Next() { -// // do something with e.Value -// } -package list - -// Element is an element of a linked list. -type Element[T any] struct { - // Next and previous pointers in the doubly-linked list of elements. - // To simplify the implementation, internally a list l is implemented - // as a ring, such that &l.root is both the next element of the last - // list element (l.Back()) and the previous element of the first list - // element (l.Front()). - next, prev *Element[T] - - // The list to which this element belongs. - list *List[T] - - // The value stored with this element. - Value T -} - -// Next returns the next list element or nil. -func (e *Element[T]) Next() *Element[T] { - if p := e.next; e.list != nil && p != &e.list.root { - return p - } - return nil -} - -// Prev returns the previous list element or nil. -func (e *Element[T]) Prev() *Element[T] { - if p := e.prev; e.list != nil && p != &e.list.root { - return p - } - return nil -} - -// List represents a doubly linked list. -// The zero value for List is an empty list ready to use. -type List[T any] struct { - root Element[T] // sentinel list element, only &root, root.prev, and root.next are used - len int // current list length excluding (this) sentinel element -} - -// Init initializes or clears list l. -func (l *List[T]) Init() *List[T] { - l.root.next = &l.root - l.root.prev = &l.root - l.len = 0 - return l -} - -// New returns an initialized list. -func New[T any]() *List[T] { return new(List[T]).Init() } - -// Len returns the number of elements of list l. -// The complexity is O(1). -func (l *List[T]) Len() int { return l.len } - -// Front returns the first element of list l or nil if the list is empty. -func (l *List[T]) Front() *Element[T] { - if l.len == 0 { - return nil - } - return l.root.next -} - -// Back returns the last element of list l or nil if the list is empty. -func (l *List[T]) Back() *Element[T] { - if l.len == 0 { - return nil - } - return l.root.prev -} - -// lazyInit lazily initializes a zero List value. -func (l *List[T]) lazyInit() { - if l.root.next == nil { - l.Init() - } -} - -// insert inserts e after at, increments l.len, and returns e. -func (l *List[T]) insert(e, at *Element[T]) *Element[T] { - e.prev = at - e.next = at.next - e.prev.next = e - e.next.prev = e - e.list = l - l.len++ - return e -} - -// insertValue is a convenience wrapper for insert(&Element{Value: v}, at). -func (l *List[T]) insertValue(v T, at *Element[T]) *Element[T] { - return l.insert(&Element[T]{Value: v}, at) -} - -// remove removes e from its list, decrements l.len -func (l *List[T]) remove(e *Element[T]) { - e.prev.next = e.next - e.next.prev = e.prev - e.next = nil // avoid memory leaks - e.prev = nil // avoid memory leaks - e.list = nil - l.len-- -} - -// move moves e to next to at. -func (l *List[T]) move(e, at *Element[T]) { - if e == at { - return - } - e.prev.next = e.next - e.next.prev = e.prev - - e.prev = at - e.next = at.next - e.prev.next = e - e.next.prev = e -} - -// Remove removes e from l if e is an element of list l. -// It returns the element value e.Value. -// The element must not be nil. -func (l *List[T]) Remove(e *Element[T]) T { - if e.list == l { - // if e.list == l, l must have been initialized when e was inserted - // in l or l == nil (e is a zero Element) and l.remove will crash - l.remove(e) - } - return e.Value -} - -// PushFront inserts a new element e with value v at the front of list l and returns e. -func (l *List[T]) PushFront(v T) *Element[T] { - l.lazyInit() - return l.insertValue(v, &l.root) -} - -// PushBack inserts a new element e with value v at the back of list l and returns e. -func (l *List[T]) PushBack(v T) *Element[T] { - l.lazyInit() - return l.insertValue(v, l.root.prev) -} - -// InsertBefore inserts a new element e with value v immediately before mark and returns e. -// If mark is not an element of l, the list is not modified. -// The mark must not be nil. -func (l *List[T]) InsertBefore(v T, mark *Element[T]) *Element[T] { - if mark.list != l { - return nil - } - // see comment in List.Remove about initialization of l - return l.insertValue(v, mark.prev) -} - -// InsertAfter inserts a new element e with value v immediately after mark and returns e. -// If mark is not an element of l, the list is not modified. -// The mark must not be nil. -func (l *List[T]) InsertAfter(v T, mark *Element[T]) *Element[T] { - if mark.list != l { - return nil - } - // see comment in List.Remove about initialization of l - return l.insertValue(v, mark) -} - -// MoveToFront moves element e to the front of list l. -// If e is not an element of l, the list is not modified. -// The element must not be nil. -func (l *List[T]) MoveToFront(e *Element[T]) { - if e.list != l || l.root.next == e { - return - } - // see comment in List.Remove about initialization of l - l.move(e, &l.root) -} - -// MoveToBack moves element e to the back of list l. -// If e is not an element of l, the list is not modified. -// The element must not be nil. -func (l *List[T]) MoveToBack(e *Element[T]) { - if e.list != l || l.root.prev == e { - return - } - // see comment in List.Remove about initialization of l - l.move(e, l.root.prev) -} - -// MoveBefore moves element e to its new position before mark. -// If e or mark is not an element of l, or e == mark, the list is not modified. -// The element and mark must not be nil. -func (l *List[T]) MoveBefore(e, mark *Element[T]) { - if e.list != l || e == mark || mark.list != l { - return - } - l.move(e, mark.prev) -} - -// MoveAfter moves element e to its new position after mark. -// If e or mark is not an element of l, or e == mark, the list is not modified. -// The element and mark must not be nil. -func (l *List[T]) MoveAfter(e, mark *Element[T]) { - if e.list != l || e == mark || mark.list != l { - return - } - l.move(e, mark) -} - -// PushBackList inserts a copy of another list at the back of list l. -// The lists l and other may be the same. They must not be nil. -func (l *List[T]) PushBackList(other *List[T]) { - l.lazyInit() - for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() { - l.insertValue(e.Value, l.root.prev) - } -} - -// PushFrontList inserts a copy of another list at the front of list l. -// The lists l and other may be the same. They must not be nil. -func (l *List[T]) PushFrontList(other *List[T]) { - l.lazyInit() - for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() { - l.insertValue(e.Value, &l.root) - } -} diff --git a/common/cache/lrucache.go b/common/lru/lrucache.go similarity index 98% rename from common/cache/lrucache.go rename to common/lru/lrucache.go index d71cc3b9..d23277c2 100644 --- a/common/cache/lrucache.go +++ b/common/lru/lrucache.go @@ -1,4 +1,4 @@ -package cache +package lru // Modified by https://github.com/die-net/lrucache @@ -6,8 +6,7 @@ import ( "sync" "time" - "github.com/metacubex/mihomo/common/generics/list" - + list "github.com/bahlo/generic-list-go" "github.com/samber/lo" ) diff --git a/common/cache/lrucache_test.go b/common/lru/lrucache_test.go similarity index 99% rename from common/cache/lrucache_test.go rename to common/lru/lrucache_test.go index 4cbc1ff8..340b3da3 100644 --- a/common/cache/lrucache_test.go +++ b/common/lru/lrucache_test.go @@ -1,4 +1,4 @@ -package cache +package lru import ( "testing" diff --git a/common/net/bufconn.go b/common/net/bufconn.go index 37c8ba25..b7e98e04 100644 --- a/common/net/bufconn.go +++ b/common/net/bufconn.go @@ -84,9 +84,9 @@ func (c *BufferedConn) ReadCached() *buf.Buffer { // call in sing/common/bufio.C length := c.r.Buffered() b, _ := c.r.Peek(length) _, _ = c.r.Discard(length) - c.r = nil // drop bufio.Reader to let gc can clean up its internal buf return buf.As(b) } + c.r = nil // drop bufio.Reader to let gc can clean up its internal buf return nil } diff --git a/common/net/bufconn_unsafe.go b/common/net/bufconn_unsafe.go new file mode 100644 index 00000000..349321df --- /dev/null +++ b/common/net/bufconn_unsafe.go @@ -0,0 +1,34 @@ +package net + +import ( + "io" + "unsafe" +) + +// bufioReader copy from stdlib bufio/bufio.go +// This structure has remained unchanged from go1.5 to go1.21. +type bufioReader struct { + buf []byte + rd io.Reader // reader provided by the client + r, w int // buf read and write positions + err error + lastByte int // last byte read for UnreadByte; -1 means invalid + lastRuneSize int // size of last rune read for UnreadRune; -1 means invalid +} + +func (c *BufferedConn) AppendData(buf []byte) (ok bool) { + b := (*bufioReader)(unsafe.Pointer(c.r)) + pos := len(b.buf) - b.w - len(buf) + if pos >= -b.r { // len(b.buf)-(b.w - b.r) >= len(buf) + if pos < 0 { // len(b.buf)-b.w < len(buf) + // Slide existing data to beginning. + copy(b.buf, b.buf[b.r:b.w]) + b.w -= b.r + b.r = 0 + } + + b.w += copy(b.buf[b.w:], buf) + return true + } + return false +} diff --git a/common/net/earlyconn.go b/common/net/earlyconn.go new file mode 100644 index 00000000..c9a42819 --- /dev/null +++ b/common/net/earlyconn.go @@ -0,0 +1,67 @@ +package net + +import ( + "net" + "sync" + "sync/atomic" + "unsafe" + + "github.com/metacubex/mihomo/common/buf" +) + +type earlyConn struct { + ExtendedConn // only expose standard N.ExtendedConn function to outside + resFunc func() error + resOnce sync.Once + resErr error +} + +func (conn *earlyConn) Response() error { + conn.resOnce.Do(func() { + conn.resErr = conn.resFunc() + }) + return conn.resErr +} + +func (conn *earlyConn) Read(b []byte) (n int, err error) { + err = conn.Response() + if err != nil { + return 0, err + } + return conn.ExtendedConn.Read(b) +} + +func (conn *earlyConn) ReadBuffer(buffer *buf.Buffer) (err error) { + err = conn.Response() + if err != nil { + return err + } + return conn.ExtendedConn.ReadBuffer(buffer) +} + +func (conn *earlyConn) Upstream() any { + return conn.ExtendedConn +} + +func (conn *earlyConn) Success() bool { + // atomic visit sync.Once.done + return atomic.LoadUint32((*uint32)(unsafe.Pointer(&conn.resOnce))) == 1 && conn.resErr == nil +} + +func (conn *earlyConn) ReaderReplaceable() bool { + return conn.Success() +} + +func (conn *earlyConn) ReaderPossiblyReplaceable() bool { + return !conn.Success() +} + +func (conn *earlyConn) WriterReplaceable() bool { + return true +} + +var _ ExtendedConn = (*earlyConn)(nil) + +func NewEarlyConn(c net.Conn, f func() error) net.Conn { + return &earlyConn{ExtendedConn: NewExtendedConn(c), resFunc: f} +} diff --git a/common/pool/alloc.go b/common/pool/alloc.go index 5722b047..80927a2c 100644 --- a/common/pool/alloc.go +++ b/common/pool/alloc.go @@ -12,22 +12,28 @@ var defaultAllocator = NewAllocator() // Allocator for incoming frames, optimized to prevent overwriting after zeroing type Allocator struct { - buffers []sync.Pool + buffers [11]sync.Pool } // NewAllocator initiates a []byte allocator for frames less than 65536 bytes, // the waste(memory fragmentation) of space allocation is guaranteed to be // no more than 50%. func NewAllocator() *Allocator { - alloc := new(Allocator) - alloc.buffers = make([]sync.Pool, 17) // 1B -> 64K - for k := range alloc.buffers { - i := k - alloc.buffers[k].New = func() any { - return make([]byte, 1< 64K + {New: func() any { return new([1 << 6]byte) }}, + {New: func() any { return new([1 << 7]byte) }}, + {New: func() any { return new([1 << 8]byte) }}, + {New: func() any { return new([1 << 9]byte) }}, + {New: func() any { return new([1 << 10]byte) }}, + {New: func() any { return new([1 << 11]byte) }}, + {New: func() any { return new([1 << 12]byte) }}, + {New: func() any { return new([1 << 13]byte) }}, + {New: func() any { return new([1 << 14]byte) }}, + {New: func() any { return new([1 << 15]byte) }}, + {New: func() any { return new([1 << 16]byte) }}, + }, } - return alloc } // Get a []byte from pool with most appropriate cap @@ -40,12 +46,42 @@ func (alloc *Allocator) Get(size int) []byte { case size > 65536: return make([]byte, size) default: - bits := msb(size) - if size == 1< 64 { + index = msb(size) + if size != 1< 65536 { return nil } - + bits := msb(cap(buf)) if cap(buf) != 1< 0 { go f.pullLoop() } @@ -129,7 +133,7 @@ func (f *Fetcher[V]) Update() (V, bool, error) { now := time.Now() hash := md5.Sum(buf) if bytes.Equal(f.hash[:], hash[:]) { - f.UpdatedAt = &now + f.UpdatedAt = now _ = os.Chtimes(f.vehicle.Path(), now, now) return lo.Empty[V](), true, nil } @@ -145,23 +149,31 @@ func (f *Fetcher[V]) Update() (V, bool, error) { } } - f.UpdatedAt = &now + f.UpdatedAt = now f.hash = hash return contents, false, nil } func (f *Fetcher[V]) Destroy() error { - if f.ticker != nil { + if f.interval > 0 { f.done <- struct{}{} } return nil } func (f *Fetcher[V]) pullLoop() { + initialInterval := f.interval - time.Since(f.UpdatedAt) + if initialInterval < minInterval { + initialInterval = minInterval + } + + timer := time.NewTimer(initialInterval) + defer timer.Stop() for { select { - case <-f.ticker.C: + case <-timer.C: + timer.Reset(f.interval) elm, same, err := f.Update() if err != nil { log.Errorln("[Provider] %s pull error: %s", f.Name(), err.Error()) @@ -178,7 +190,6 @@ func (f *Fetcher[V]) pullLoop() { f.OnUpdate(elm) } case <-f.done: - f.ticker.Stop() return } } @@ -197,17 +208,12 @@ func safeWrite(path string, buf []byte) error { } func NewFetcher[V any](name string, interval time.Duration, vehicle types.Vehicle, parser Parser[V], onUpdate func(V)) *Fetcher[V] { - var ticker *time.Ticker - if interval != 0 { - ticker = time.NewTicker(interval) - } return &Fetcher[V]{ name: name, - ticker: ticker, vehicle: vehicle, parser: parser, - done: make(chan struct{}, 1), + done: make(chan struct{}, 8), OnUpdate: onUpdate, interval: interval, } diff --git a/component/sniffer/dispatcher.go b/component/sniffer/dispatcher.go index 29bea088..96e9272c 100644 --- a/component/sniffer/dispatcher.go +++ b/component/sniffer/dispatcher.go @@ -8,7 +8,7 @@ import ( "sync" "time" - "github.com/metacubex/mihomo/common/cache" + "github.com/metacubex/mihomo/common/lru" N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/component/trie" C "github.com/metacubex/mihomo/constant" @@ -29,7 +29,7 @@ type SnifferDispatcher struct { sniffers map[sniffer.Sniffer]SnifferConfig forceDomain *trie.DomainSet skipSNI *trie.DomainSet - skipList *cache.LruCache[string, uint8] + skipList *lru.LruCache[string, uint8] rwMux sync.RWMutex forceDnsMapping bool parsePureIp bool @@ -202,7 +202,7 @@ func NewSnifferDispatcher(snifferConfig map[sniffer.Type]SnifferConfig, enable: true, forceDomain: forceDomain, skipSNI: skipSNI, - skipList: cache.New(cache.WithSize[string, uint8](128), cache.WithAge[string, uint8](600)), + skipList: lru.New(lru.WithSize[string, uint8](128), lru.WithAge[string, uint8](600)), forceDnsMapping: forceDnsMapping, parsePureIp: parsePureIp, sniffers: make(map[sniffer.Sniffer]SnifferConfig, 0), diff --git a/config/config.go b/config/config.go index 76ff9d68..9cba5d09 100644 --- a/config/config.go +++ b/config/config.go @@ -29,6 +29,7 @@ import ( tlsC "github.com/metacubex/mihomo/component/tls" "github.com/metacubex/mihomo/component/trie" C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/constant/features" providerTypes "github.com/metacubex/mihomo/constant/provider" snifferTypes "github.com/metacubex/mihomo/constant/sniffer" "github.com/metacubex/mihomo/dns" @@ -39,6 +40,7 @@ import ( RP "github.com/metacubex/mihomo/rules/provider" T "github.com/metacubex/mihomo/tunnel" + orderedmap "github.com/wk8/go-ordered-map/v2" "gopkg.in/yaml.v3" ) @@ -112,9 +114,10 @@ type DNS struct { Listen string `yaml:"listen"` EnhancedMode C.DNSMode `yaml:"enhanced-mode"` DefaultNameserver []dns.NameServer `yaml:"default-nameserver"` + CacheAlgorithm string `yaml:"cache-algorithm"` FakeIPRange *fakeip.Pool Hosts *trie.DomainTrie[resolver.HostValue] - NameServerPolicy map[string][]dns.NameServer + NameServerPolicy *orderedmap.OrderedMap[string, []dns.NameServer] ProxyServerNameserver []dns.NameServer } @@ -193,29 +196,35 @@ type RawNTP struct { } type RawDNS struct { - Enable bool `yaml:"enable"` - PreferH3 bool `yaml:"prefer-h3"` - IPv6 bool `yaml:"ipv6"` - IPv6Timeout uint `yaml:"ipv6-timeout"` - UseHosts bool `yaml:"use-hosts"` - NameServer []string `yaml:"nameserver"` - Fallback []string `yaml:"fallback"` - FallbackFilter RawFallbackFilter `yaml:"fallback-filter"` - Listen string `yaml:"listen"` - EnhancedMode C.DNSMode `yaml:"enhanced-mode"` - FakeIPRange string `yaml:"fake-ip-range"` - FakeIPFilter []string `yaml:"fake-ip-filter"` - DefaultNameserver []string `yaml:"default-nameserver"` - NameServerPolicy map[string]any `yaml:"nameserver-policy"` - ProxyServerNameserver []string `yaml:"proxy-server-nameserver"` + Enable bool `yaml:"enable" json:"enable"` + PreferH3 bool `yaml:"prefer-h3" json:"prefer-h3"` + IPv6 bool `yaml:"ipv6" json:"ipv6"` + IPv6Timeout uint `yaml:"ipv6-timeout" json:"ipv6-timeout"` + UseHosts bool `yaml:"use-hosts" json:"use-hosts"` + NameServer []string `yaml:"nameserver" json:"nameserver"` + Fallback []string `yaml:"fallback" json:"fallback"` + FallbackFilter RawFallbackFilter `yaml:"fallback-filter" json:"fallback-filter"` + Listen string `yaml:"listen" json:"listen"` + EnhancedMode C.DNSMode `yaml:"enhanced-mode" json:"enhanced-mode"` + FakeIPRange string `yaml:"fake-ip-range" json:"fake-ip-range"` + FakeIPFilter []string `yaml:"fake-ip-filter" json:"fake-ip-filter"` + DefaultNameserver []string `yaml:"default-nameserver" json:"default-nameserver"` + CacheAlgorithm string `yaml:"cache-algorithm" json:"cache-algorithm"` + NameServerPolicy *orderedmap.OrderedMap[string, any] `yaml:"nameserver-policy" json:"nameserver-policy"` + ProxyServerNameserver []string `yaml:"proxy-server-nameserver" json:"proxy-server-nameserver"` } type RawFallbackFilter struct { - GeoIP bool `yaml:"geoip"` - GeoIPCode string `yaml:"geoip-code"` - IPCIDR []string `yaml:"ipcidr"` - Domain []string `yaml:"domain"` - GeoSite []string `yaml:"geosite"` + GeoIP bool `yaml:"geoip" json:"geoip"` + GeoIPCode string `yaml:"geoip-code" json:"geoip-code"` + IPCIDR []string `yaml:"ipcidr" json:"ipcidr"` + Domain []string `yaml:"domain" json:"domain"` + GeoSite []string `yaml:"geosite" json:"geosite"` +} + +type RawClashForAndroid struct { + AppendSystemDNS bool `yaml:"append-system-dns" json:"append-system-dns"` + UiSubtitlePattern string `yaml:"ui-subtitle-pattern" json:"ui-subtitle-pattern"` } type RawTun struct { @@ -263,23 +272,23 @@ type RawTuicServer struct { } type RawConfig struct { - Port int `yaml:"port"` - SocksPort int `yaml:"socks-port"` - RedirPort int `yaml:"redir-port"` - TProxyPort int `yaml:"tproxy-port"` - MixedPort int `yaml:"mixed-port"` + Port int `yaml:"port" json:"port"` + SocksPort int `yaml:"socks-port" json:"socks-port"` + RedirPort int `yaml:"redir-port" json:"redir-port"` + TProxyPort int `yaml:"tproxy-port" json:"tproxy-port"` + MixedPort int `yaml:"mixed-port" json:"mixed-port"` ShadowSocksConfig string `yaml:"ss-config"` VmessConfig string `yaml:"vmess-config"` InboundTfo bool `yaml:"inbound-tfo"` InboundMPTCP bool `yaml:"inbound-mptcp"` - Authentication []string `yaml:"authentication"` + Authentication []string `yaml:"authentication" json:"authentication"` SkipAuthPrefixes []netip.Prefix `yaml:"skip-auth-prefixes"` - AllowLan bool `yaml:"allow-lan"` - BindAddress string `yaml:"bind-address"` - Mode T.TunnelMode `yaml:"mode"` - UnifiedDelay bool `yaml:"unified-delay"` - LogLevel log.LogLevel `yaml:"log-level"` - IPv6 bool `yaml:"ipv6"` + AllowLan bool `yaml:"allow-lan" json:"allow-lan"` + BindAddress string `yaml:"bind-address" json:"bind-address"` + Mode T.TunnelMode `yaml:"mode" json:"mode"` + UnifiedDelay bool `yaml:"unified-delay" json:"unified-delay"` + LogLevel log.LogLevel `yaml:"log-level" json:"log-level"` + IPv6 bool `yaml:"ipv6" json:"ipv6"` ExternalController string `yaml:"external-controller"` ExternalControllerTLS string `yaml:"external-controller-tls"` ExternalUI string `yaml:"external-ui"` @@ -289,20 +298,20 @@ type RawConfig struct { Interface string `yaml:"interface-name"` RoutingMark int `yaml:"routing-mark"` Tunnels []LC.Tunnel `yaml:"tunnels"` - GeodataMode bool `yaml:"geodata-mode"` - GeodataLoader string `yaml:"geodata-loader"` + GeodataMode bool `yaml:"geodata-mode" json:"geodata-mode"` + GeodataLoader string `yaml:"geodata-loader" json:"geodata-loader"` TCPConcurrent bool `yaml:"tcp-concurrent" json:"tcp-concurrent"` FindProcessMode P.FindProcessMode `yaml:"find-process-mode" json:"find-process-mode"` GlobalClientFingerprint string `yaml:"global-client-fingerprint"` GlobalUA string `yaml:"global-ua"` KeepAliveInterval int `yaml:"keep-alive-interval"` - Sniffer RawSniffer `yaml:"sniffer"` + Sniffer RawSniffer `yaml:"sniffer" json:"sniffer"` ProxyProvider map[string]map[string]any `yaml:"proxy-providers"` RuleProvider map[string]map[string]any `yaml:"rule-providers"` - Hosts map[string]any `yaml:"hosts"` - NTP RawNTP `yaml:"ntp"` - DNS RawDNS `yaml:"dns"` + Hosts map[string]any `yaml:"hosts" json:"hosts"` + NTP RawNTP `yaml:"ntp" json:"ntp"` + DNS RawDNS `yaml:"dns" json:"dns"` Tun RawTun `yaml:"tun"` TuicServer RawTuicServer `yaml:"tuic-server"` EBpf EBpf `yaml:"ebpf"` @@ -316,6 +325,8 @@ type RawConfig struct { SubRules map[string][]string `yaml:"sub-rules"` RawTLS TLS `yaml:"tls"` Listeners []map[string]any `yaml:"listeners"` + + ClashForAndroid RawClashForAndroid `yaml:"clash-for-android" json:"clash-for-android"` } type GeoXUrl struct { @@ -381,7 +392,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { ProxyGroup: []map[string]any{}, TCPConcurrent: false, FindProcessMode: P.FindProcessStrict, - GlobalUA: "mihomo", + GlobalUA: "clash.meta", Tun: RawTun{ Enable: false, Device: "", @@ -545,7 +556,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { config.DNS = dnsCfg err = parseTun(rawCfg.Tun, config.General) - if err != nil { + if !features.CMFA && err != nil { return nil, err } @@ -664,6 +675,7 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[ proxies["DIRECT"] = adapter.NewProxy(outbound.NewDirect()) proxies["REJECT"] = adapter.NewProxy(outbound.NewReject()) + proxies["REJECT-DROP"] = adapter.NewProxy(outbound.NewRejectDrop()) proxies["COMPATIBLE"] = adapter.NewProxy(outbound.NewCompatible()) proxies["PASS"] = adapter.NewProxy(outbound.NewPass()) proxyList = append(proxyList, "DIRECT", "REJECT") @@ -917,9 +929,9 @@ func parseHosts(cfg *RawConfig) (*trie.DomainTrie[resolver.HostValue], error) { if len(cfg.Hosts) != 0 { for domain, anyValue := range cfg.Hosts { - if str, ok := anyValue.(string); ok && str == "mihomo" { + if str, ok := anyValue.(string); ok && str == "lan" { if addrs, err := net.InterfaceAddrs(); err != nil { - log.Errorln("insert mihomo to host error: %s", err) + log.Errorln("insert lan to host error: %s", err) } else { ips := make([]netip.Addr, 0) for _, addr := range addrs { @@ -1085,12 +1097,13 @@ func parsePureDNSServer(server string) string { } } } -func parseNameServerPolicy(nsPolicy map[string]any, ruleProviders map[string]providerTypes.RuleProvider, preferH3 bool) (map[string][]dns.NameServer, error) { - policy := map[string][]dns.NameServer{} - updatedPolicy := make(map[string]interface{}) +func parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], ruleProviders map[string]providerTypes.RuleProvider, 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,})?`) - for k, v := range nsPolicy { + for pair := nsPolicy.Oldest(); pair != nil; pair = pair.Next() { + k, v := pair.Key, pair.Value if strings.Contains(k, ",") { if strings.Contains(k, "geosite:") { subkeys := strings.Split(k, ":") @@ -1098,7 +1111,7 @@ func parseNameServerPolicy(nsPolicy map[string]any, ruleProviders map[string]pro subkeys = strings.Split(subkeys[0], ",") for _, subkey := range subkeys { newKey := "geosite:" + subkey - updatedPolicy[newKey] = v + updatedPolicy.Store(newKey, v) } } else if strings.Contains(k, "rule-set:") { subkeys := strings.Split(k, ":") @@ -1106,20 +1119,21 @@ func parseNameServerPolicy(nsPolicy map[string]any, ruleProviders map[string]pro subkeys = strings.Split(subkeys[0], ",") for _, subkey := range subkeys { newKey := "rule-set:" + subkey - updatedPolicy[newKey] = v + updatedPolicy.Store(newKey, v) } } else if re.MatchString(k) { subkeys := strings.Split(k, ",") for _, subkey := range subkeys { - updatedPolicy[subkey] = v + updatedPolicy.Store(subkey, v) } } } else { - updatedPolicy[k] = v + updatedPolicy.Store(k, v) } } - for domain, server := range updatedPolicy { + for pair := updatedPolicy.Oldest(); pair != nil; pair = pair.Next() { + domain, server := pair.Key, pair.Value servers, err := utils.ToStringSlice(server) if err != nil { return nil, err @@ -1144,7 +1158,7 @@ func parseNameServerPolicy(nsPolicy map[string]any, ruleProviders map[string]pro } } } - policy[domain] = nameservers + policy.Store(domain, nameservers) } return policy, nil @@ -1330,6 +1344,12 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[resolver.HostValue], rul dnsCfg.Hosts = hosts } + if cfg.CacheAlgorithm == "" || cfg.CacheAlgorithm == "lru" { + dnsCfg.CacheAlgorithm = "lru" + } else { + dnsCfg.CacheAlgorithm = "arc" + } + return dnsCfg, nil } diff --git a/config/utils.go b/config/utils.go index 5a4fecbf..66bf3441 100644 --- a/config/utils.go +++ b/config/utils.go @@ -14,12 +14,13 @@ import ( "github.com/metacubex/mihomo/adapter/outboundgroup" "github.com/metacubex/mihomo/common/structure" mihomoHttp "github.com/metacubex/mihomo/component/http" + C "github.com/metacubex/mihomo/constant" ) func downloadForBytes(url string) ([]byte, error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, url, http.MethodGet, http.Header{"User-Agent": {"mihomo"}}, nil) + resp, err := mihomoHttp.HttpRequest(ctx, url, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) if err != nil { return nil, err } diff --git a/constant/adapters.go b/constant/adapters.go index 5cf6e07c..68d4aa4e 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -18,6 +18,7 @@ import ( const ( Direct AdapterType = iota Reject + RejectDrop Compatible Pass @@ -42,10 +43,11 @@ const ( ) const ( - DefaultTCPTimeout = 5 * time.Second - DefaultUDPTimeout = DefaultTCPTimeout - DefaultTLSTimeout = DefaultTCPTimeout - DefaultMaxHealthCheckUrlNum = 16 + DefaultTCPTimeout = 5 * time.Second + DefaultDropTime = 12 * DefaultTCPTimeout + DefaultUDPTimeout = DefaultTCPTimeout + DefaultTLSTimeout = DefaultTCPTimeout + DefaultTestURL = "https://cp.cloudflare.com/generate_204" ) var ErrNotSupport = errors.New("no support") @@ -147,21 +149,13 @@ type DelayHistory struct { type DelayHistoryStoreType int -const ( - OriginalHistory DelayHistoryStoreType = iota - ExtraHistory - DropHistory -) - type Proxy interface { ProxyAdapter - Alive() bool AliveForTestUrl(url string) bool DelayHistory() []DelayHistory ExtraDelayHistory() map[string][]DelayHistory - LastDelay() uint16 LastDelayForTestUrl(url string) uint16 - URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16], store DelayHistoryStoreType) (uint16, error) + URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16]) (uint16, error) // Deprecated: use DialContext instead. Dial(metadata *Metadata) (Conn, error) @@ -179,6 +173,8 @@ func (at AdapterType) String() string { return "Direct" case Reject: return "Reject" + case RejectDrop: + return "RejectDrop" case Compatible: return "Compatible" case Pass: diff --git a/constant/features/cmfa.go b/constant/features/cmfa.go new file mode 100644 index 00000000..9a81d82f --- /dev/null +++ b/constant/features/cmfa.go @@ -0,0 +1,5 @@ +//go:build cmfa + +package features + +const CMFA = true diff --git a/constant/features/cmfa_stub.go b/constant/features/cmfa_stub.go new file mode 100644 index 00000000..f7ef0827 --- /dev/null +++ b/constant/features/cmfa_stub.go @@ -0,0 +1,5 @@ +//go:build !cmfa + +package features + +const CMFA = false diff --git a/constant/features/low_memory.go b/constant/features/low_memory.go index 0d252113..2f0f4d50 100644 --- a/constant/features/low_memory.go +++ b/constant/features/low_memory.go @@ -1,6 +1,5 @@ //go:build with_low_memory + package features -func init() { - TAGS = append(TAGS, "with_low_memory") -} +const WithLowMemory = true diff --git a/constant/features/low_memory_stub.go b/constant/features/low_memory_stub.go new file mode 100644 index 00000000..00b0b947 --- /dev/null +++ b/constant/features/low_memory_stub.go @@ -0,0 +1,5 @@ +//go:build !with_low_memory + +package features + +const WithLowMemory = false diff --git a/constant/features/no_fake_tcp.go b/constant/features/no_fake_tcp.go index f536a066..a9e09728 100644 --- a/constant/features/no_fake_tcp.go +++ b/constant/features/no_fake_tcp.go @@ -2,6 +2,4 @@ package features -func init() { - TAGS = append(TAGS, "no_fake_tcp") -} +const NoFakeTCP = true diff --git a/constant/features/no_fake_tcp_stub.go b/constant/features/no_fake_tcp_stub.go new file mode 100644 index 00000000..f1620a2b --- /dev/null +++ b/constant/features/no_fake_tcp_stub.go @@ -0,0 +1,5 @@ +//go:build !no_fake_tcp + +package features + +const NoFakeTCP = false diff --git a/constant/features/tags.go b/constant/features/tags.go index c81f6d4e..12a5fbdd 100644 --- a/constant/features/tags.go +++ b/constant/features/tags.go @@ -1,3 +1,17 @@ package features -var TAGS = make([]string, 0, 0) +func Tags() (tags []string) { + if CMFA { + tags = append(tags, "cmfa") + } + if WithLowMemory { + tags = append(tags, "with_low_memory") + } + if NoFakeTCP { + tags = append(tags, "no_fake_tcp") + } + if WithGVisor { + tags = append(tags, "with_gvisor") + } + return +} diff --git a/constant/features/with_gvisor.go b/constant/features/with_gvisor.go index 1b3417b3..c00cc34e 100644 --- a/constant/features/with_gvisor.go +++ b/constant/features/with_gvisor.go @@ -2,6 +2,4 @@ package features -func init() { - TAGS = append(TAGS, "with_gvisor") -} +const WithGVisor = true diff --git a/constant/features/with_gvisor_stub.go b/constant/features/with_gvisor_stub.go new file mode 100644 index 00000000..5684c1b1 --- /dev/null +++ b/constant/features/with_gvisor_stub.go @@ -0,0 +1,5 @@ +//go:build !with_gvisor + +package features + +const WithGVisor = false diff --git a/constant/metadata.go b/constant/metadata.go index 4b547a81..09a2f152 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -147,6 +147,9 @@ type Metadata struct { SpecialProxy string `json:"specialProxy"` SpecialRules string `json:"specialRules"` RemoteDst string `json:"remoteDestination"` + + RawSrcAddr net.Addr `json:"-"` + RawDstAddr net.Addr `json:"-"` // Only domain rule SniffHost string `json:"sniffHost"` } diff --git a/dns/dhcp.go b/dns/dhcp.go index dc1344f5..d4944a96 100644 --- a/dns/dhcp.go +++ b/dns/dhcp.go @@ -1,3 +1,5 @@ +//go:build !(android && cmfa) + package dns import ( diff --git a/dns/enhancer.go b/dns/enhancer.go index 82fdd35a..a8c0a5ac 100644 --- a/dns/enhancer.go +++ b/dns/enhancer.go @@ -3,7 +3,7 @@ package dns import ( "net/netip" - "github.com/metacubex/mihomo/common/cache" + "github.com/metacubex/mihomo/common/lru" "github.com/metacubex/mihomo/component/fakeip" C "github.com/metacubex/mihomo/constant" ) @@ -11,7 +11,7 @@ import ( type ResolverEnhancer struct { mode C.DNSMode fakePool *fakeip.Pool - mapping *cache.LruCache[netip.Addr, string] + mapping *lru.LruCache[netip.Addr, string] } func (h *ResolverEnhancer) FakeIPEnabled() bool { @@ -105,11 +105,11 @@ func (h *ResolverEnhancer) StoreFakePoolState() { func NewEnhancer(cfg Config) *ResolverEnhancer { var fakePool *fakeip.Pool - var mapping *cache.LruCache[netip.Addr, string] + var mapping *lru.LruCache[netip.Addr, string] if cfg.EnhancedMode != C.DNSNormal { fakePool = cfg.Pool - mapping = cache.New(cache.WithSize[netip.Addr, string](4096)) + mapping = lru.New(lru.WithSize[netip.Addr, string](4096)) } return &ResolverEnhancer{ diff --git a/dns/middleware.go b/dns/middleware.go index f8e051a0..b55adc6b 100644 --- a/dns/middleware.go +++ b/dns/middleware.go @@ -5,7 +5,7 @@ import ( "strings" "time" - "github.com/metacubex/mihomo/common/cache" + "github.com/metacubex/mihomo/common/lru" "github.com/metacubex/mihomo/common/nnip" "github.com/metacubex/mihomo/component/fakeip" R "github.com/metacubex/mihomo/component/resolver" @@ -21,7 +21,7 @@ type ( middleware func(next handler) handler ) -func withHosts(hosts R.Hosts, mapping *cache.LruCache[netip.Addr, string]) middleware { +func withHosts(hosts R.Hosts, mapping *lru.LruCache[netip.Addr, string]) middleware { return func(next handler) handler { return func(ctx *context.DNSContext, r *D.Msg) (*D.Msg, error) { q := r.Question[0] @@ -98,7 +98,7 @@ func withHosts(hosts R.Hosts, mapping *cache.LruCache[netip.Addr, string]) middl } } -func withMapping(mapping *cache.LruCache[netip.Addr, string]) middleware { +func withMapping(mapping *lru.LruCache[netip.Addr, string]) middleware { return func(next handler) handler { return func(ctx *context.DNSContext, r *D.Msg) (*D.Msg, error) { q := r.Question[0] diff --git a/dns/patch_android.go b/dns/patch_android.go new file mode 100644 index 00000000..2e9a6b9f --- /dev/null +++ b/dns/patch_android.go @@ -0,0 +1,81 @@ +//go:build android && cmfa + +package dns + +import ( + "context" + + D "github.com/miekg/dns" + + "github.com/metacubex/mihomo/common/lru" + "github.com/metacubex/mihomo/component/dhcp" + "github.com/metacubex/mihomo/component/resolver" +) + +const SystemDNSPlaceholder = "system" + +var systemResolver *Resolver +var isolateHandler handler + +var _ dnsClient = (*dhcpClient)(nil) + +type dhcpClient struct { + enable bool +} + +func (d *dhcpClient) Address() string { + return SystemDNSPlaceholder +} + +func (d *dhcpClient) Exchange(m *D.Msg) (msg *D.Msg, err error) { + return d.ExchangeContext(context.Background(), m) +} + +func (d *dhcpClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { + if s := systemResolver; s != nil { + return s.ExchangeContext(ctx, m) + } + + return nil, dhcp.ErrNotFound +} + +func ServeDNSWithDefaultServer(msg *D.Msg) (*D.Msg, error) { + if h := isolateHandler; h != nil { + return handlerWithContext(context.Background(), h, msg) + } + + return nil, D.ErrTime +} + +func FlushCacheWithDefaultResolver() { + if r := resolver.DefaultResolver; r != nil { + r.(*Resolver).lruCache = lru.New[string, *D.Msg](lru.WithSize[string, *D.Msg](4096), lru.WithStale[string, *D.Msg](true)) + } +} + +func UpdateSystemDNS(addr []string) { + if len(addr) == 0 { + systemResolver = nil + } + + ns := make([]NameServer, 0, len(addr)) + for _, d := range addr { + ns = append(ns, NameServer{Addr: d}) + } + + systemResolver = NewResolver(Config{Main: ns}) +} + +func UpdateIsolateHandler(resolver *Resolver, mapper *ResolverEnhancer) { + if resolver == nil { + isolateHandler = nil + + return + } + + isolateHandler = NewHandler(resolver, mapper) +} + +func newDHCPClient(ifaceName string) *dhcpClient { + return &dhcpClient{enable: ifaceName == SystemDNSPlaceholder} +} diff --git a/dns/patch_common.go b/dns/patch_common.go new file mode 100644 index 00000000..fae1e126 --- /dev/null +++ b/dns/patch_common.go @@ -0,0 +1,6 @@ +//go:build !(android && cmfa) + +package dns + +func UpdateIsolateHandler(resolver *Resolver, mapper *ResolverEnhancer) { +} diff --git a/dns/policy.go b/dns/policy.go index a8b423e1..a58123e3 100644 --- a/dns/policy.go +++ b/dns/policy.go @@ -1,30 +1,50 @@ package dns -type Policy struct { - data []dnsClient +import ( + "github.com/metacubex/mihomo/component/trie" + C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/constant/provider" +) + +type dnsPolicy interface { + Match(domain string) []dnsClient } -func (p *Policy) GetData() []dnsClient { - return p.data +type domainTriePolicy struct { + *trie.DomainTrie[[]dnsClient] } -func (p *Policy) Compare(p2 *Policy) int { - if p2 == nil { - return 1 +func (p domainTriePolicy) Match(domain string) []dnsClient { + record := p.DomainTrie.Search(domain) + if record != nil { + return record.Data() } - l1 := len(p.data) - l2 := len(p2.data) - if l1 == l2 { - return 0 - } - if l1 > l2 { - return 1 - } - return -1 + return nil } -func NewPolicy(data []dnsClient) *Policy { - return &Policy{ - data: data, - } +type geositePolicy struct { + matcher fallbackDomainFilter + inverse bool + dnsClients []dnsClient +} + +func (p geositePolicy) Match(domain string) []dnsClient { + matched := p.matcher.Match(domain) + if matched != p.inverse { + return p.dnsClients + } + return nil +} + +type domainSetPolicy struct { + domainSetProvider provider.RuleProvider + dnsClients []dnsClient +} + +func (p domainSetPolicy) Match(domain string) []dnsClient { + metadata := &C.Metadata{Host: domain} + if ok := p.domainSetProvider.Match(metadata); ok { + return p.dnsClients + } + return nil } diff --git a/dns/resolver.go b/dns/resolver.go index 610a06f0..e1ca95dd 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -7,7 +7,8 @@ import ( "strings" "time" - "github.com/metacubex/mihomo/common/cache" + "github.com/metacubex/mihomo/common/arc" + "github.com/metacubex/mihomo/common/lru" "github.com/metacubex/mihomo/component/fakeip" "github.com/metacubex/mihomo/component/geodata/router" "github.com/metacubex/mihomo/component/resolver" @@ -18,6 +19,8 @@ import ( D "github.com/miekg/dns" "github.com/samber/lo" + orderedmap "github.com/wk8/go-ordered-map/v2" + "golang.org/x/exp/maps" "golang.org/x/sync/singleflight" ) @@ -26,22 +29,16 @@ type dnsClient interface { Address() string } +type dnsCache interface { + GetWithExpire(key string) (*D.Msg, time.Time, bool) + SetWithExpire(key string, value *D.Msg, expire time.Time) +} + type result struct { Msg *D.Msg Error error } -type geositePolicyRecord struct { - matcher fallbackDomainFilter - policy *Policy - inversedMatching bool -} - -type domainSetPolicyRecord struct { - domainSetProvider provider.RuleProvider - policy *Policy -} - type Resolver struct { ipv6 bool ipv6Timeout time.Duration @@ -51,10 +48,8 @@ type Resolver struct { fallbackDomainFilters []fallbackDomainFilter fallbackIPFilters []fallbackIPFilter group singleflight.Group - lruCache *cache.LruCache[string, *D.Msg] - policy *trie.DomainTrie[*Policy] - domainSetPolicy []domainSetPolicyRecord - geositePolicy []geositePolicyRecord + cache dnsCache + policy []dnsPolicy proxyServer []dnsClient } @@ -151,8 +146,9 @@ func (r *Resolver) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, e }() q := m.Question[0] - cacheM, expireTime, hit := r.lruCache.GetWithExpire(q.String()) + cacheM, expireTime, hit := r.cache.GetWithExpire(q.String()) if hit { + log.Debugln("[DNS] cache hit for %s, expire at %s", q.Name, expireTime.Format("2006-01-02 15:04:05")) now := time.Now() msg = cacheM.Copy() if expireTime.Before(now) { @@ -192,7 +188,7 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M msg.Extra = lo.Filter(msg.Extra, func(rr D.RR, index int) bool { return rr.Header().Rrtype != D.TypeOPT }) - putMsgToCache(r.lruCache, q.String(), q, msg) + putMsgToCache(r.cache, q.String(), q, msg) } }() @@ -258,22 +254,9 @@ func (r *Resolver) matchPolicy(m *D.Msg) []dnsClient { return nil } - record := r.policy.Search(domain) - if record != nil { - p := record.Data() - return p.GetData() - } - - for _, geositeRecord := range r.geositePolicy { - matched := geositeRecord.matcher.Match(domain) - if matched != geositeRecord.inversedMatching { - return geositeRecord.policy.GetData() - } - } - metadata := &C.Metadata{Host: domain} - for _, domainSetRecord := range r.domainSetPolicy { - if ok := domainSetRecord.domainSetProvider.Match(metadata); ok { - return domainSetRecord.policy.GetData() + for _, policy := range r.policy { + if dnsClients := policy.Match(domain); len(dnsClients) > 0 { + return dnsClients } } return nil @@ -395,6 +378,23 @@ type NameServer struct { PreferH3 bool } +func (ns NameServer) Equal(ns2 NameServer) bool { + defer func() { + // C.ProxyAdapter compare maybe panic, just ignore + recover() + }() + if ns.Net == ns2.Net && + ns.Addr == ns2.Addr && + ns.Interface == ns2.Interface && + ns.ProxyAdapter == ns2.ProxyAdapter && + ns.ProxyName == ns2.ProxyName && + maps.Equal(ns.Params, ns2.Params) && + ns.PreferH3 == ns2.PreferH3 { + return true + } + return false +} + type FallbackFilter struct { GeoIP bool GeoIPCode string @@ -404,76 +404,139 @@ type FallbackFilter struct { } type Config struct { - Main, Fallback []NameServer - Default []NameServer - ProxyServer []NameServer - IPv6 bool - IPv6Timeout uint - EnhancedMode C.DNSMode - FallbackFilter FallbackFilter - Pool *fakeip.Pool - Hosts *trie.DomainTrie[resolver.HostValue] - Policy map[string][]NameServer - DomainSetPolicy map[provider.RuleProvider][]NameServer - GeositePolicy map[router.DomainMatcher][]NameServer + Main, Fallback []NameServer + Default []NameServer + ProxyServer []NameServer + IPv6 bool + IPv6Timeout uint + EnhancedMode C.DNSMode + FallbackFilter FallbackFilter + Pool *fakeip.Pool + Hosts *trie.DomainTrie[resolver.HostValue] + Policy *orderedmap.OrderedMap[string, []NameServer] + RuleProviders map[string]provider.RuleProvider + CacheAlgorithm string } func NewResolver(config Config) *Resolver { + var cache dnsCache + if config.CacheAlgorithm == "lru" { + cache = lru.New(lru.WithSize[string, *D.Msg](4096), lru.WithStale[string, *D.Msg](true)) + } else { + cache = arc.New(arc.WithSize[string, *D.Msg](4096)) + } defaultResolver := &Resolver{ main: transform(config.Default, nil), - lruCache: cache.New(cache.WithSize[string, *D.Msg](4096), cache.WithStale[string, *D.Msg](true)), + cache: cache, ipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond, } + var nameServerCache []struct { + NameServer + dnsClient + } + cacheTransform := func(nameserver []NameServer) (result []dnsClient) { + LOOP: + for _, ns := range nameserver { + for _, nsc := range nameServerCache { + if nsc.NameServer.Equal(ns) { + result = append(result, nsc.dnsClient) + continue LOOP + } + } + // not in cache + dc := transform([]NameServer{ns}, defaultResolver) + if len(dc) > 0 { + dc := dc[0] + nameServerCache = append(nameServerCache, struct { + NameServer + dnsClient + }{NameServer: ns, dnsClient: dc}) + result = append(result, dc) + } + } + return + } + + if config.CacheAlgorithm == "" || config.CacheAlgorithm == "lru" { + cache = lru.New(lru.WithSize[string, *D.Msg](4096), lru.WithStale[string, *D.Msg](true)) + } else { + cache = arc.New(arc.WithSize[string, *D.Msg](4096)) + } r := &Resolver{ ipv6: config.IPv6, - main: transform(config.Main, defaultResolver), - lruCache: cache.New(cache.WithSize[string, *D.Msg](4096), cache.WithStale[string, *D.Msg](true)), + main: cacheTransform(config.Main), + cache: cache, hosts: config.Hosts, ipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond, } if len(config.Fallback) != 0 { - r.fallback = transform(config.Fallback, defaultResolver) + r.fallback = cacheTransform(config.Fallback) } if len(config.ProxyServer) != 0 { - r.proxyServer = transform(config.ProxyServer, defaultResolver) + r.proxyServer = cacheTransform(config.ProxyServer) } - if len(config.Policy) != 0 { - r.policy = trie.New[*Policy]() - for domain, nameserver := range config.Policy { - if strings.HasPrefix(strings.ToLower(domain), "geosite:") { - groupname := domain[8:] - inverse := false - if strings.HasPrefix(groupname, "!") { - inverse = true - groupname = groupname[1:] - } - log.Debugln("adding geosite policy: %s inversed %t", groupname, inverse) - matcher, err := NewGeoSite(groupname) - if err != nil { - continue - } - r.geositePolicy = append(r.geositePolicy, geositePolicyRecord{ - matcher: matcher, - policy: NewPolicy(transform(nameserver, defaultResolver)), - inversedMatching: inverse, - }) - } else { - _ = r.policy.Insert(domain, NewPolicy(transform(nameserver, defaultResolver))) + if config.Policy.Len() != 0 { + r.policy = make([]dnsPolicy, 0) + + var triePolicy *trie.DomainTrie[[]dnsClient] + insertTriePolicy := func() { + if triePolicy != nil { + triePolicy.Optimize() + r.policy = append(r.policy, domainTriePolicy{triePolicy}) + triePolicy = nil } } - r.policy.Optimize() - } - if len(config.DomainSetPolicy) > 0 { - for p, n := range config.DomainSetPolicy { - r.domainSetPolicy = append(r.domainSetPolicy, domainSetPolicyRecord{ - domainSetProvider: p, - policy: NewPolicy(transform(n, defaultResolver)), - }) + + for pair := config.Policy.Oldest(); pair != nil; pair = pair.Next() { + domain, nameserver := pair.Key, pair.Value + domain = strings.ToLower(domain) + + if temp := strings.Split(domain, ":"); len(temp) == 2 { + prefix := temp[0] + key := temp[1] + switch strings.ToLower(prefix) { + case "rule-set": + if p, ok := config.RuleProviders[key]; ok { + insertTriePolicy() + r.policy = append(r.policy, domainSetPolicy{ + domainSetProvider: p, + dnsClients: cacheTransform(nameserver), + }) + continue + } else { + log.Warnln("can't found ruleset policy: %s", key) + } + case "geosite": + inverse := false + if strings.HasPrefix(key, "!") { + inverse = true + key = key[1:] + } + log.Debugln("adding geosite policy: %s inversed %t", key, inverse) + matcher, err := NewGeoSite(key) + if err != nil { + log.Warnln("adding geosite policy %s error: %s", key, err) + continue + } + insertTriePolicy() + r.policy = append(r.policy, geositePolicy{ + matcher: matcher, + inverse: inverse, + dnsClients: cacheTransform(nameserver), + }) + continue // skip triePolicy new + } + } + if triePolicy == nil { + triePolicy = trie.New[[]dnsClient]() + } + _ = triePolicy.Insert(domain, cacheTransform(nameserver)) } + insertTriePolicy() } fallbackIPFilters := []fallbackIPFilter{} @@ -506,9 +569,8 @@ func NewProxyServerHostResolver(old *Resolver) *Resolver { r := &Resolver{ ipv6: old.ipv6, main: old.proxyServer, - lruCache: old.lruCache, + cache: old.cache, hosts: old.hosts, - policy: trie.New[*Policy](), ipv6Timeout: old.ipv6Timeout, } return r diff --git a/dns/server.go b/dns/server.go index 1cf58d4d..d45fb5eb 100644 --- a/dns/server.go +++ b/dns/server.go @@ -6,6 +6,7 @@ import ( "net" "github.com/metacubex/mihomo/common/sockopt" + "github.com/metacubex/mihomo/constant/features" "github.com/metacubex/mihomo/context" "github.com/metacubex/mihomo/log" @@ -49,6 +50,10 @@ func (s *Server) SetHandler(handler handler) { } func ReCreateServer(addr string, resolver *Resolver, mapper *ResolverEnhancer) { + if features.CMFA { + UpdateIsolateHandler(resolver, mapper) + } + if addr == address && resolver != nil { handler := NewHandler(resolver, mapper) server.SetHandler(handler) diff --git a/dns/util.go b/dns/util.go index c354a73d..f2243917 100644 --- a/dns/util.go +++ b/dns/util.go @@ -11,7 +11,6 @@ import ( "strings" "time" - "github.com/metacubex/mihomo/common/cache" N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/common/nnip" "github.com/metacubex/mihomo/common/picker" @@ -51,7 +50,7 @@ func updateTTL(records []D.RR, ttl uint32) { } } -func putMsgToCache(c *cache.LruCache[string, *D.Msg], key string, q D.Question, msg *D.Msg) { +func putMsgToCache(c dnsCache, key string, q D.Question, msg *D.Msg) { // skip dns cache for acme challenge if q.Qtype == D.TypeTXT && strings.HasPrefix(q.Name, "_acme-challenge.") { log.Debugln("[DNS] dns cache ignored because of acme challenge for: %s", q.Name) diff --git a/docs/config.yaml b/docs/config.yaml index d5e1174a..bdb822f7 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -78,7 +78,7 @@ hosts: # '.dev': 127.0.0.1 # 'alpha.mihomo.dev': '::1' # test.com: [1.1.1.1, 2.2.2.2] -# mihomo.lan: mihomo # mihomo 为特别字段,将加入本地所有网卡的地址 +# home.lan: lan # lan 为特别字段,将加入本地所有网卡的地址 # baidu.com: google.com # 只允许配置一个别名 profile: # 存储 select 选择记录 @@ -183,6 +183,7 @@ tunnels: # one line config # DNS配置 dns: + cache-algorithm: arc enable: false # 关闭将使用系统 DNS prefer-h3: true # 开启 DoH 支持 HTTP/3,将并发尝试 listen: 0.0.0.0:53 # 开启 DNS 服务器监听 diff --git a/go.mod b/go.mod index da45cd28..b4462c5c 100644 --- a/go.mod +++ b/go.mod @@ -5,51 +5,53 @@ go 1.20 require ( github.com/3andne/restls-client-go v0.1.6 github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da - github.com/cilium/ebpf v0.12.0 + github.com/bahlo/generic-list-go v0.2.0 + github.com/cilium/ebpf v0.12.3 github.com/coreos/go-iptables v0.7.0 github.com/dlclark/regexp2 v1.10.0 github.com/go-chi/chi/v5 v5.0.10 github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.3 - github.com/gobwas/ws v1.3.0 + github.com/gobwas/ws v1.3.1 github.com/gofrs/uuid/v5 v5.0.0 - github.com/insomniacslk/dhcp v0.0.0-20230908212754-65c27093e38a + github.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c github.com/jpillora/backoff v1.0.0 - github.com/klauspost/cpuid/v2 v2.2.5 + github.com/klauspost/cpuid/v2 v2.2.6 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.39.1-0.20231019030608-fd969d66f16b - github.com/metacubex/sing-quic v0.0.0-20231008050747-a684db516966 + github.com/metacubex/quic-go v0.40.1-0.20231130135418-0c1b47cf9394 + github.com/metacubex/sing-quic v0.0.0-20231130141855-0022295e524b github.com/metacubex/sing-shadowsocks v0.2.5 github.com/metacubex/sing-shadowsocks2 v0.1.4 github.com/metacubex/sing-tun v0.1.15-0.20231103033938-170591e8d5bd github.com/metacubex/sing-vmess v0.1.9-0.20230921005247-a0488d7dac74 github.com/metacubex/sing-wireguard v0.0.0-20231001110902-321836559170 - github.com/miekg/dns v1.1.56 + github.com/miekg/dns v1.1.57 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/v2 v2.5.1 + github.com/puzpuzpuz/xsync/v3 v3.0.2 + github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 - github.com/sagernet/sing v0.2.14 - github.com/sagernet/sing-mux v0.1.3 + github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c + github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 github.com/sagernet/sing-shadowtls v0.1.4 github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 github.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f github.com/samber/lo v1.38.1 - github.com/shirou/gopsutil/v3 v3.23.9 + github.com/shirou/gopsutil/v3 v3.23.10 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.8.4 + github.com/wk8/go-ordered-map/v2 v2.1.8 github.com/zhangyunhao116/fastrand v0.3.0 - go.etcd.io/bbolt v1.3.7 go.uber.org/automaxprocs v1.5.3 - golang.org/x/crypto v0.14.0 - golang.org/x/exp v0.0.0-20231006140011-7918f672742d - golang.org/x/net v0.17.0 - golang.org/x/sync v0.4.0 - golang.org/x/sys v0.13.0 + golang.org/x/crypto v0.16.0 + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa + golang.org/x/net v0.18.0 + golang.org/x/sync v0.5.0 + golang.org/x/sys v0.15.0 google.golang.org/protobuf v1.31.0 gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.2.1 @@ -60,6 +62,7 @@ require ( github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect github.com/ajg/form v1.5.1 // indirect github.com/andybalholm/brotli v1.0.5 // indirect + github.com/buger/jsonparser v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 // indirect github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect @@ -72,12 +75,13 @@ require ( github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/pool v0.2.1 // indirect github.com/google/btree v1.1.2 // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/josharian/native v1.1.0 // indirect github.com/klauspost/compress v1.16.7 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mdlayher/socket v0.4.1 // indirect github.com/metacubex/gvisor v0.0.0-20231001104248-0f672c3fb8d8 // indirect github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect @@ -86,7 +90,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/quic-go/qpack v0.4.0 // indirect - github.com/quic-go/qtls-go1-20 v0.3.4 // indirect + github.com/quic-go/qtls-go1-20 v0.4.1 // indirect github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect @@ -102,10 +106,10 @@ require ( gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect go.uber.org/mock v0.3.0 // indirect go4.org/netipx v0.0.0-20230824141953-6213f710f925 // indirect - golang.org/x/mod v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.14.0 // indirect + golang.org/x/tools v0.15.0 // indirect ) -replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20231001053806-1230641572b9 +replace github.com/sagernet/sing => github.com/metacubex/sing v0.0.0-20231118023733-957d84f17d2c diff --git a/go.sum b/go.sum index 6a6356c1..0e0ab68c 100644 --- a/go.sum +++ b/go.sum @@ -10,12 +10,16 @@ github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= +github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.12.0 h1:oQEuIQIXgYhe1v7sYUG0P9vtJTYZLLdA6tiQmrOB1mo= -github.com/cilium/ebpf v0.12.0/go.mod h1:u9H29/Iq+8cy70YqI6p5pfADkFl3vdnV2qXDg5JL0Zo= +github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4= +github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM= github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8= github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -53,8 +57,8 @@ github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.3.0 h1:sbeU3Y4Qzlb+MOzIe6mQGf7QR4Hkv6ZD0qhGkBFL2O0= -github.com/gobwas/ws v1.3.0/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= +github.com/gobwas/ws v1.3.1 h1:Qi34dfLMWJbiKaNbDVzM9x27nZBjmkaW6i4+Ku+pGVU= +github.com/gobwas/ws v1.3.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M= github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -64,16 +68,18 @@ github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 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= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 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-20230908212754-65c27093e38a h1:S33o3djA1nPRd+d/bf7jbbXytXuK/EoXow7+aa76grQ= -github.com/insomniacslk/dhcp v0.0.0-20230908212754-65c27093e38a/go.mod h1:zmdm3sTSDP3vOOX3CEWRkkRHtKr1DxBx+J1OQFoDQQs= +github.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c h1:PgxFEySCI41sH0mB7/2XswdXbUykQsRUGod8Rn+NubM= +github.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI= +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= @@ -81,14 +87,16 @@ github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2E github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= +github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/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= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= @@ -97,12 +105,12 @@ github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvO github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88= github.com/metacubex/gvisor v0.0.0-20231001104248-0f672c3fb8d8 h1:npBvaPAT145UY8682AzpUMWpdIxJti/WPLjy7gCiYYs= github.com/metacubex/gvisor v0.0.0-20231001104248-0f672c3fb8d8/go.mod h1:ZR6Gas7P1GcADCVBc1uOrA0bLQqDDyp70+63fD/BE2c= -github.com/metacubex/quic-go v0.39.1-0.20231019030608-fd969d66f16b h1:uZ++sW8yg7Fr/Wvmmrb/V+SfxvRs0iMC+2+u2bRmO8g= -github.com/metacubex/quic-go v0.39.1-0.20231019030608-fd969d66f16b/go.mod h1:4pe6cY+nAMFU/Uxn1rfnxNIowsaJGDQ3uyy4VuiPkP4= -github.com/metacubex/sing v0.0.0-20231001053806-1230641572b9 h1:F0+IuW0tZ96QHEmrebXAdYnz7ab7Gz4l5yYC4g6Cg8k= -github.com/metacubex/sing v0.0.0-20231001053806-1230641572b9/go.mod h1:GQ673iPfUnkbK/dIPkfd1Xh1MjOGo36gkl/mkiHY7Jg= -github.com/metacubex/sing-quic v0.0.0-20231008050747-a684db516966 h1:wbOsbU3kfD5LRuJIntJwEPmgGSQukof8CgLNypi8az8= -github.com/metacubex/sing-quic v0.0.0-20231008050747-a684db516966/go.mod h1:GU7g2AZesXItk4CspDP8Dc7eGtlA2GVDihyCwsUXRSo= +github.com/metacubex/quic-go v0.40.1-0.20231130135418-0c1b47cf9394 h1:dIT+KB2hknBCrwVAXPeY9tpzzkOZP5m40yqUteRT6/Y= +github.com/metacubex/quic-go v0.40.1-0.20231130135418-0c1b47cf9394/go.mod h1:F/t8VnA47xoia8ABlNA4InkZjssvFJ5p6E6jKdbkgAs= +github.com/metacubex/sing v0.0.0-20231118023733-957d84f17d2c h1:SZwaf42NVCIDaHw5X2HOnNcEiK9ao6yO+N4zYyKfXe8= +github.com/metacubex/sing v0.0.0-20231118023733-957d84f17d2c/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= +github.com/metacubex/sing-quic v0.0.0-20231130141855-0022295e524b h1:7XXoEePvxfkQN9b2wB8UXU3uzb9uL8syEFF7A9VAKKQ= +github.com/metacubex/sing-quic v0.0.0-20231130141855-0022295e524b/go.mod h1:Gu5/zqZDd5G1AUtoV2yjAPWOEy7zwbU2DBUjdxJh0Kw= github.com/metacubex/sing-shadowsocks v0.2.5 h1:O2RRSHlKGEpAVG/OHJQxyHqDy8uvvdCW/oW2TDBOIhc= github.com/metacubex/sing-shadowsocks v0.2.5/go.mod h1:Xz2uW9BEYGEoA8B4XEpoxt7ERHClFCwsMAvWaruoyMo= github.com/metacubex/sing-shadowsocks2 v0.1.4 h1:OOCf8lgsVcpTOJUeaFAMzyKVebaQOBnKirDdUdBoKIE= @@ -113,8 +121,8 @@ github.com/metacubex/sing-vmess v0.1.9-0.20230921005247-a0488d7dac74 h1:FtupiyFk github.com/metacubex/sing-vmess v0.1.9-0.20230921005247-a0488d7dac74/go.mod h1:8EWBZpc+qNvf5gmvjAtMHK1/DpcWqzfcBL842K00BsM= github.com/metacubex/sing-wireguard v0.0.0-20231001110902-321836559170 h1:DBGA0hmrP4pVIwLiXUONdphjcppED+plmVaKf1oqkwk= github.com/metacubex/sing-wireguard v0.0.0-20231001110902-321836559170/go.mod h1:/VbJfbdLnANE+SKXyMk/96sTRrD4GdFLh5mkegqqFcY= -github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= -github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= +github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= +github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= github.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU= 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= @@ -137,19 +145,21 @@ 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/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU= -github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= +github.com/puzpuzpuz/xsync/v3 v3.0.2 h1:3yESHrRFYr6xzkz61LLkvNiPFXxJEAABanTQpKbAaew= +github.com/puzpuzpuz/xsync/v3 v3.0.2/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.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg= -github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= +github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= +github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= 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/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA= github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= -github.com/sagernet/sing-mux v0.1.3 h1:fAf7PZa2A55mCeh0KKM02f1k2Y4vEmxuZZ/51ahkkLA= -github.com/sagernet/sing-mux v0.1.3/go.mod h1:wGeIeiiFLx4HUM5LAg65wrNZ/X1muOimqK0PEhNbPi0= +github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 h1:ncKb5tVOsCQgCsv6UpsA0jinbNb5OQ5GMPJlyQP3EHM= +github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07/go.mod h1:u/MZf32xPG8jEKe3t+xUV67EBnKtDtCaPhsJQOQGUYU= github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k= github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as= @@ -164,8 +174,8 @@ github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg= github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s= -github.com/shirou/gopsutil/v3 v3.23.9 h1:ZI5bWVeu2ep4/DIxB4U9okeYJ7zp/QLTO4auRb/ty/E= -github.com/shirou/gopsutil/v3 v3.23.9/go.mod h1:x/NWSb71eMcjFIO0vhyGW5nZ7oSIgVjrCnADckb85GA= +github.com/shirou/gopsutil/v3 v3.23.10 h1:/N42opWlYzegYaVkWejXWJpbzKv2JDy3mrgGzKsh9hM= +github.com/shirou/gopsutil/v3 v3.23.10/go.mod h1:JIE26kpucQi+innVlAUnIEOSBhBUkirr5b44yr55+WE= 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= @@ -199,14 +209,14 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17 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/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= +github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= github.com/zhangyunhao116/fastrand v0.3.0 h1:7bwe124xcckPulX6fxtr2lFdO2KQqaefdtbk+mqO/Ig= github.com/zhangyunhao116/fastrand v0.3.0/go.mod h1:0v5KgHho0VE6HU192HnY15de/oDS8UrbBChIFjIhBtc= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ= -go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= -go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8= go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0= go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= @@ -215,21 +225,21 @@ go4.org/netipx v0.0.0-20230824141953-6213f710f925 h1:eeQDDVKFkx0g4Hyy8pHgmZaK0Eq go4.org/netipx v0.0.0-20230824141953-6213f710f925/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.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= 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.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -245,17 +255,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.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 6ea02989..72d7380e 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -7,12 +7,9 @@ import ( "os" "runtime" "strconv" - "strings" "sync" "time" - "github.com/metacubex/mihomo/ntp" - "github.com/metacubex/mihomo/adapter" "github.com/metacubex/mihomo/adapter/inbound" "github.com/metacubex/mihomo/adapter/outboundgroup" @@ -28,6 +25,7 @@ import ( "github.com/metacubex/mihomo/component/trie" "github.com/metacubex/mihomo/config" C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/constant/features" "github.com/metacubex/mihomo/constant/provider" "github.com/metacubex/mihomo/dns" "github.com/metacubex/mihomo/listener" @@ -36,6 +34,7 @@ import ( "github.com/metacubex/mihomo/listener/inner" "github.com/metacubex/mihomo/listener/tproxy" "github.com/metacubex/mihomo/log" + "github.com/metacubex/mihomo/ntp" "github.com/metacubex/mihomo/tunnel" ) @@ -172,7 +171,9 @@ func updateListeners(general *config.General, listeners map[string]C.InboundList listener.ReCreateHTTP(general.Port, tunnel.Tunnel) listener.ReCreateSocks(general.SocksPort, tunnel.Tunnel) listener.ReCreateRedir(general.RedirPort, tunnel.Tunnel) - listener.ReCreateAutoRedir(general.EBpf.AutoRedir, tunnel.Tunnel) + if !features.CMFA { + listener.ReCreateAutoRedir(general.EBpf.AutoRedir, tunnel.Tunnel) + } listener.ReCreateTProxy(general.TProxyPort, tunnel.Tunnel) listener.ReCreateMixed(general.MixedPort, tunnel.Tunnel) listener.ReCreateShadowSocks(general.ShadowSocksConfig, tunnel.Tunnel) @@ -208,25 +209,6 @@ func updateDNS(c *config.DNS, ruleProvider map[string]provider.RuleProvider, gen dns.ReCreateServer("", nil, nil) return } - policy := make(map[string][]dns.NameServer) - domainSetPolicies := make(map[provider.RuleProvider][]dns.NameServer) - for key, nameservers := range c.NameServerPolicy { - temp := strings.Split(key, ":") - if len(temp) == 2 { - prefix := temp[0] - key := temp[1] - switch strings.ToLower(prefix) { - case "rule-set": - if p, ok := ruleProvider[key]; ok { - domainSetPolicies[p] = nameservers - } - case "geosite": - // TODO: - } - } else { - policy[key] = nameservers - } - } cfg := dns.Config{ Main: c.NameServer, Fallback: c.Fallback, @@ -242,10 +224,11 @@ func updateDNS(c *config.DNS, ruleProvider map[string]provider.RuleProvider, gen Domain: c.FallbackFilter.Domain, GeoSite: c.FallbackFilter.GeoSite, }, - Default: c.DefaultNameserver, - Policy: c.NameServerPolicy, - ProxyServer: c.ProxyServerNameserver, - DomainSetPolicy: domainSetPolicies, + Default: c.DefaultNameserver, + Policy: c.NameServerPolicy, + ProxyServer: c.ProxyServerNameserver, + RuleProviders: ruleProvider, + CacheAlgorithm: c.CacheAlgorithm, } r := dns.NewResolver(cfg) @@ -330,6 +313,9 @@ func loadProxyProvider(proxyProviders map[string]provider.ProxyProvider) { go func() { defer func() { <-ch; wg.Done() }() loadProvider(proxyProvider) + if proxyProvider.VehicleType() == provider.Compatible { + go proxyProvider.HealthCheck() + } }() } diff --git a/hub/route/proxies.go b/hub/route/proxies.go index 759e64d2..48359749 100644 --- a/hub/route/proxies.go +++ b/hub/route/proxies.go @@ -125,7 +125,7 @@ func getProxyDelay(w http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(timeout)) defer cancel() - delay, err := proxy.URLTest(ctx, url, expectedStatus, C.ExtraHistory) + delay, err := proxy.URLTest(ctx, url, expectedStatus) if ctx.Err() != nil { render.Status(r, http.StatusGatewayTimeout) render.JSON(w, r, ErrRequestTimeout) diff --git a/hub/route/server.go b/hub/route/server.go index d510e986..6ececddb 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -89,7 +89,7 @@ func Start(addr string, tlsAddr string, secret string, r.Mount("/connections", connectionRouter()) r.Mount("/providers/proxies", proxyProviderRouter()) r.Mount("/providers/rules", ruleProviderRouter()) - r.Mount("/cache", cacheRouter()) + r.Mount("/lru", cacheRouter()) r.Mount("/dns", dnsRouter()) r.Mount("/restart", restartRouter()) r.Mount("/upgrade", upgradeRouter()) diff --git a/hub/updater/updater.go b/hub/updater/updater.go index a3bc9a42..1967af42 100644 --- a/hub/updater/updater.go +++ b/hub/updater/updater.go @@ -17,6 +17,7 @@ import ( mihomoHttp "github.com/metacubex/mihomo/component/http" "github.com/metacubex/mihomo/constant" + C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" "github.com/klauspost/cpuid/v2" @@ -231,7 +232,7 @@ const MaxPackageFileSize = 32 * 1024 * 1024 func downloadPackageFile() (err error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, packageURL, http.MethodGet, http.Header{"User-Agent": {"mihomo"}}, nil) + resp, err := mihomoHttp.HttpRequest(ctx, packageURL, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) if err != nil { return fmt.Errorf("http request failed: %w", err) } @@ -412,7 +413,7 @@ func copyFile(src, dst string) error { func getLatestVersion() (version string, err error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() - resp, err := mihomoHttp.HttpRequest(ctx, versionURL, http.MethodGet, http.Header{"User-Agent": {"mihomo"}}, nil) + resp, err := mihomoHttp.HttpRequest(ctx, versionURL, http.MethodGet, http.Header{"User-Agent": {C.UA}}, nil) if err != nil { return "", fmt.Errorf("get Latest Version fail: %w", err) } diff --git a/listener/config/hysteria2.go b/listener/config/hysteria2.go index 5520babc..898204c6 100644 --- a/listener/config/hysteria2.go +++ b/listener/config/hysteria2.go @@ -1,6 +1,10 @@ package config -import "encoding/json" +import ( + "github.com/metacubex/mihomo/listener/sing" + + "encoding/json" +) type Hysteria2Server struct { Enable bool `yaml:"enable" json:"enable"` @@ -17,6 +21,7 @@ type Hysteria2Server struct { IgnoreClientBandwidth bool `yaml:"ignore-client-bandwidth" json:"ignore-client-bandwidth,omitempty"` Masquerade string `yaml:"masquerade" json:"masquerade,omitempty"` CWND int `yaml:"cwnd" json:"cwnd,omitempty"` + MuxOption sing.MuxOption `yaml:"mux-option" json:"mux-option,omitempty"` } func (h Hysteria2Server) String() string { diff --git a/listener/config/shadowsocks.go b/listener/config/shadowsocks.go index 60540bbd..c5c60f10 100644 --- a/listener/config/shadowsocks.go +++ b/listener/config/shadowsocks.go @@ -1,15 +1,18 @@ package config import ( + "github.com/metacubex/mihomo/listener/sing" + "encoding/json" ) type ShadowsocksServer struct { - Enable bool - Listen string - Password string - Cipher string - Udp bool + Enable bool + Listen string + Password string + Cipher string + Udp bool + MuxOption sing.MuxOption `yaml:"mux-option" json:"mux-option,omitempty"` } func (t ShadowsocksServer) String() string { diff --git a/listener/config/tuic.go b/listener/config/tuic.go index 191cb59c..14a46809 100644 --- a/listener/config/tuic.go +++ b/listener/config/tuic.go @@ -1,6 +1,8 @@ package config import ( + "github.com/metacubex/mihomo/listener/sing" + "encoding/json" ) @@ -18,6 +20,7 @@ type TuicServer struct { MaxUdpRelayPacketSize int `yaml:"max-udp-relay-packet-size" json:"max-udp-relay-packet-size,omitempty"` MaxDatagramFrameSize int `yaml:"max-datagram-frame-size" json:"max-datagram-frame-size,omitempty"` CWND int `yaml:"cwnd" json:"cwnd,omitempty"` + MuxOption sing.MuxOption `yaml:"mux-option" json:"mux-option,omitempty"` } func (t TuicServer) String() string { diff --git a/listener/config/vmess.go b/listener/config/vmess.go index 1cf2d46c..810d6bc1 100644 --- a/listener/config/vmess.go +++ b/listener/config/vmess.go @@ -1,6 +1,8 @@ package config import ( + "github.com/metacubex/mihomo/listener/sing" + "encoding/json" ) @@ -17,6 +19,7 @@ type VmessServer struct { WsPath string Certificate string PrivateKey string + MuxOption sing.MuxOption `yaml:"mux-option" json:"mux-option,omitempty"` } func (t VmessServer) String() string { diff --git a/listener/http/patch_android.go b/listener/http/patch_android.go new file mode 100644 index 00000000..b1b8bfc7 --- /dev/null +++ b/listener/http/patch_android.go @@ -0,0 +1,9 @@ +//go:build android && cmfa + +package http + +import "net" + +func (l *Listener) Listener() net.Listener { + return l.listener +} diff --git a/listener/http/proxy.go b/listener/http/proxy.go index 76da4d95..45f53d6b 100644 --- a/listener/http/proxy.go +++ b/listener/http/proxy.go @@ -7,21 +7,21 @@ import ( "strings" "github.com/metacubex/mihomo/adapter/inbound" - "github.com/metacubex/mihomo/common/cache" + "github.com/metacubex/mihomo/common/lru" N "github.com/metacubex/mihomo/common/net" C "github.com/metacubex/mihomo/constant" authStore "github.com/metacubex/mihomo/listener/auth" "github.com/metacubex/mihomo/log" ) -func HandleConn(c net.Conn, tunnel C.Tunnel, cache *cache.LruCache[string, bool], additions ...inbound.Addition) { +func HandleConn(c net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool], additions ...inbound.Addition) { client := newClient(c, tunnel, additions...) defer client.CloseIdleConnections() conn := N.NewBufferedConn(c) keepAlive := true - trusted := cache == nil // disable authenticate if cache is nil + trusted := cache == nil // disable authenticate if lru is nil for keepAlive { request, err := ReadRequest(conn.Reader()) @@ -98,7 +98,7 @@ func HandleConn(c net.Conn, tunnel C.Tunnel, cache *cache.LruCache[string, bool] _ = conn.Close() } -func authenticate(request *http.Request, cache *cache.LruCache[string, bool]) *http.Response { +func authenticate(request *http.Request, cache *lru.LruCache[string, bool]) *http.Response { authenticator := authStore.Authenticator() if inbound.SkipAuthRemoteAddress(request.RemoteAddr) { authenticator = nil diff --git a/listener/http/server.go b/listener/http/server.go index a75e2092..be397d9a 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -4,8 +4,9 @@ import ( "net" "github.com/metacubex/mihomo/adapter/inbound" - "github.com/metacubex/mihomo/common/cache" + "github.com/metacubex/mihomo/common/lru" C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/constant/features" ) type Listener struct { @@ -47,9 +48,9 @@ func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additi return nil, err } - var c *cache.LruCache[string, bool] + var c *lru.LruCache[string, bool] if authenticate { - c = cache.New[string, bool](cache.WithAge[string, bool](30)) + c = lru.New[string, bool](lru.WithAge[string, bool](30)) } hl := &Listener{ @@ -65,6 +66,11 @@ func NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additi } continue } + if features.CMFA { + if t, ok := conn.(*net.TCPConn); ok { + t.SetKeepAlive(false) + } + } go HandleConn(conn, tunnel, c, additions...) } }() diff --git a/listener/inbound/hysteria2.go b/listener/inbound/hysteria2.go index 112d03f8..acd5f9a8 100644 --- a/listener/inbound/hysteria2.go +++ b/listener/inbound/hysteria2.go @@ -21,6 +21,7 @@ type Hysteria2Option struct { IgnoreClientBandwidth bool `inbound:"ignore-client-bandwidth,omitempty"` Masquerade string `inbound:"masquerade,omitempty"` CWND int `inbound:"cwnd,omitempty"` + MuxOption MuxOption `inbound:"mux-option,omitempty"` } func (o Hysteria2Option) Equal(config C.InboundConfig) bool { @@ -57,6 +58,7 @@ func NewHysteria2(options *Hysteria2Option) (*Hysteria2, error) { IgnoreClientBandwidth: options.IgnoreClientBandwidth, Masquerade: options.Masquerade, CWND: options.CWND, + MuxOption: options.MuxOption.Build(), }, }, nil } diff --git a/listener/inbound/mux.go b/listener/inbound/mux.go new file mode 100644 index 00000000..c4068e10 --- /dev/null +++ b/listener/inbound/mux.go @@ -0,0 +1,25 @@ +package inbound + +import "github.com/metacubex/mihomo/listener/sing" + +type MuxOption struct { + Padding bool `inbound:"padding,omitempty"` + Brutal BrutalOptions `inbound:"brutal,omitempty"` +} + +type BrutalOptions struct { + Enabled bool `inbound:"enabled,omitempty"` + Up string `inbound:"up,omitempty"` + Down string `inbound:"down,omitempty"` +} + +func (m MuxOption) Build() sing.MuxOption { + return sing.MuxOption{ + Padding: m.Padding, + Brutal: sing.BrutalOptions{ + Enabled: m.Brutal.Enabled, + Up: m.Brutal.Up, + Down: m.Brutal.Down, + }, + } +} diff --git a/listener/inbound/shadowsocks.go b/listener/inbound/shadowsocks.go index cb32dcfb..240e6419 100644 --- a/listener/inbound/shadowsocks.go +++ b/listener/inbound/shadowsocks.go @@ -9,9 +9,10 @@ import ( type ShadowSocksOption struct { BaseOption - Password string `inbound:"password"` - Cipher string `inbound:"cipher"` - UDP bool `inbound:"udp,omitempty"` + Password string `inbound:"password"` + Cipher string `inbound:"cipher"` + UDP bool `inbound:"udp,omitempty"` + MuxOption MuxOption `inbound:"mux-option,omitempty"` } func (o ShadowSocksOption) Equal(config C.InboundConfig) bool { @@ -34,11 +35,12 @@ func NewShadowSocks(options *ShadowSocksOption) (*ShadowSocks, error) { Base: base, config: options, ss: LC.ShadowsocksServer{ - Enable: true, - Listen: base.RawAddress(), - Password: options.Password, - Cipher: options.Cipher, - Udp: options.UDP, + Enable: true, + Listen: base.RawAddress(), + Password: options.Password, + Cipher: options.Cipher, + Udp: options.UDP, + MuxOption: options.MuxOption.Build(), }, }, nil } diff --git a/listener/inbound/tuic.go b/listener/inbound/tuic.go index c2a73b84..562228ee 100644 --- a/listener/inbound/tuic.go +++ b/listener/inbound/tuic.go @@ -19,6 +19,7 @@ type TuicOption struct { ALPN []string `inbound:"alpn,omitempty"` MaxUdpRelayPacketSize int `inbound:"max-udp-relay-packet-size,omitempty"` CWND int `inbound:"cwnd,omitempty"` + MuxOption MuxOption `inbound:"mux-option,omitempty"` } func (o TuicOption) Equal(config C.InboundConfig) bool { @@ -53,6 +54,7 @@ func NewTuic(options *TuicOption) (*Tuic, error) { ALPN: options.ALPN, MaxUdpRelayPacketSize: options.MaxUdpRelayPacketSize, CWND: options.CWND, + MuxOption: options.MuxOption.Build(), }, }, nil } diff --git a/listener/inbound/vmess.go b/listener/inbound/vmess.go index 3508aa3c..226a54d5 100644 --- a/listener/inbound/vmess.go +++ b/listener/inbound/vmess.go @@ -13,6 +13,7 @@ type VmessOption struct { WsPath string `inbound:"ws-path,omitempty"` Certificate string `inbound:"certificate,omitempty"` PrivateKey string `inbound:"private-key,omitempty"` + MuxOption MuxOption `inbound:"mux-option,omitempty"` } type VmessUser struct { @@ -55,6 +56,7 @@ func NewVmess(options *VmessOption) (*Vmess, error) { WsPath: options.WsPath, Certificate: options.Certificate, PrivateKey: options.PrivateKey, + MuxOption: options.MuxOption.Build(), }, }, nil } diff --git a/listener/mixed/mixed.go b/listener/mixed/mixed.go index 97d1407c..0f1b3aab 100644 --- a/listener/mixed/mixed.go +++ b/listener/mixed/mixed.go @@ -4,7 +4,7 @@ import ( "net" "github.com/metacubex/mihomo/adapter/inbound" - "github.com/metacubex/mihomo/common/cache" + "github.com/metacubex/mihomo/common/lru" N "github.com/metacubex/mihomo/common/net" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/listener/http" @@ -16,7 +16,7 @@ import ( type Listener struct { listener net.Listener addr string - cache *cache.LruCache[string, bool] + cache *lru.LruCache[string, bool] closed bool } @@ -51,7 +51,7 @@ func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener ml := &Listener{ listener: l, addr: addr, - cache: cache.New[string, bool](cache.WithAge[string, bool](30)), + cache: lru.New[string, bool](lru.WithAge[string, bool](30)), } go func() { for { @@ -69,7 +69,7 @@ func New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener return ml, nil } -func handleConn(conn net.Conn, tunnel C.Tunnel, cache *cache.LruCache[string, bool], additions ...inbound.Addition) { +func handleConn(conn net.Conn, tunnel C.Tunnel, cache *lru.LruCache[string, bool], additions ...inbound.Addition) { N.TCPKeepAlive(conn) bufConn := N.NewBufferedConn(conn) diff --git a/listener/sing/sing.go b/listener/sing/sing.go index 306bd705..65c42b6a 100644 --- a/listener/sing/sing.go +++ b/listener/sing/sing.go @@ -9,6 +9,7 @@ import ( "time" "github.com/metacubex/mihomo/adapter/inbound" + "github.com/metacubex/mihomo/adapter/outbound" N "github.com/metacubex/mihomo/common/net" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" @@ -26,11 +27,28 @@ import ( const UDPTimeout = 5 * time.Minute -type ListenerHandler struct { +type ListenerConfig struct { Tunnel C.Tunnel Type C.Type Additions []inbound.Addition UDPTimeout time.Duration + MuxOption MuxOption +} + +type MuxOption struct { + Padding bool `yaml:"padding" json:"padding,omitempty"` + Brutal BrutalOptions `yaml:"brutal" json:"brutal,omitempty"` +} + +type BrutalOptions struct { + Enabled bool `yaml:"enabled" json:"enabled"` + Up string `yaml:"up" json:"up,omitempty"` + Down string `yaml:"down" json:"down,omitempty"` +} + +type ListenerHandler struct { + ListenerConfig + muxService *mux.Service } func UpstreamMetadata(metadata M.Metadata) M.Metadata { @@ -48,22 +66,40 @@ func ConvertMetadata(metadata *C.Metadata) M.Metadata { } } +func NewListenerHandler(lc ListenerConfig) (h *ListenerHandler, err error) { + h = &ListenerHandler{ListenerConfig: lc} + h.muxService, err = mux.NewService(mux.ServiceOptions{ + NewStreamContext: func(ctx context.Context, conn net.Conn) context.Context { + return ctx + }, + Logger: log.SingLogger, + Handler: h, + Padding: lc.MuxOption.Padding, + Brutal: mux.BrutalOptions{ + Enabled: lc.MuxOption.Brutal.Enabled, + SendBPS: outbound.StringToBps(lc.MuxOption.Brutal.Up), + ReceiveBPS: outbound.StringToBps(lc.MuxOption.Brutal.Down), + }, + }) + return +} + func (h *ListenerHandler) IsSpecialFqdn(fqdn string) bool { switch fqdn { - case mux.Destination.Fqdn: - case vmess.MuxDestination.Fqdn: - case uot.MagicAddress: - case uot.LegacyMagicAddress: + case mux.Destination.Fqdn, + vmess.MuxDestination.Fqdn, + uot.MagicAddress, + uot.LegacyMagicAddress: + return true default: return false } - return true } func (h *ListenerHandler) ParseSpecialFqdn(ctx context.Context, conn net.Conn, metadata M.Metadata) error { switch metadata.Destination.Fqdn { case mux.Destination.Fqdn: - return mux.HandleConnection(ctx, h, log.SingLogger, conn, UpstreamMetadata(metadata)) + return h.muxService.NewConnection(ctx, conn, UpstreamMetadata(metadata)) case vmess.MuxDestination.Fqdn: return vmess.HandleMuxConnection(ctx, conn, h) case uot.MagicAddress: diff --git a/listener/sing_hysteria2/server.go b/listener/sing_hysteria2/server.go index 96553995..de4c95f5 100644 --- a/listener/sing_hysteria2/server.go +++ b/listener/sing_hysteria2/server.go @@ -42,10 +42,14 @@ func New(config LC.Hysteria2Server, tunnel C.Tunnel, additions ...inbound.Additi } } - h := &sing.ListenerHandler{ + h, err := sing.NewListenerHandler(sing.ListenerConfig{ Tunnel: tunnel, Type: C.HYSTERIA2, Additions: additions, + MuxOption: config.MuxOption, + }) + if err != nil { + return nil, err } sl = &Listener{false, config, nil, nil} diff --git a/listener/sing_shadowsocks/server.go b/listener/sing_shadowsocks/server.go index 5a4896af..1760e43c 100644 --- a/listener/sing_shadowsocks/server.go +++ b/listener/sing_shadowsocks/server.go @@ -50,10 +50,14 @@ func New(config LC.ShadowsocksServer, tunnel C.Tunnel, additions ...inbound.Addi udpTimeout := int64(sing.UDPTimeout.Seconds()) - h := &sing.ListenerHandler{ + h, err := sing.NewListenerHandler(sing.ListenerConfig{ Tunnel: tunnel, Type: C.SHADOWSOCKS, Additions: additions, + MuxOption: config.MuxOption, + }) + if err != nil { + return nil, err } sl = &Listener{false, config, nil, nil, nil} diff --git a/listener/sing_tun/dns.go b/listener/sing_tun/dns.go index 62a15c6c..122f5a32 100644 --- a/listener/sing_tun/dns.go +++ b/listener/sing_tun/dns.go @@ -26,7 +26,7 @@ const DefaultDnsReadTimeout = time.Second * 10 const DefaultDnsRelayTimeout = time.Second * 5 type ListenerHandler struct { - sing.ListenerHandler + *sing.ListenerHandler DnsAdds []netip.AddrPort } diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 212c3d90..2017e922 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -8,7 +8,6 @@ import ( "runtime" "strconv" "strings" - "time" "github.com/metacubex/mihomo/adapter/inbound" "github.com/metacubex/mihomo/component/dialer" @@ -150,14 +149,18 @@ func New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Lis dnsAdds = append(dnsAdds, addrPort) } + h, err := sing.NewListenerHandler(sing.ListenerConfig{ + Tunnel: tunnel, + Type: C.TUN, + Additions: additions, + }) + if err != nil { + return nil, err + } + handler := &ListenerHandler{ - ListenerHandler: sing.ListenerHandler{ - Tunnel: tunnel, - Type: C.TUN, - Additions: additions, - UDPTimeout: time.Second * time.Duration(udpTimeout), - }, - DnsAdds: dnsAdds, + ListenerHandler: h, + DnsAdds: dnsAdds, } l = &Listener{ closed: false, diff --git a/listener/sing_vmess/server.go b/listener/sing_vmess/server.go index e790e3bc..ce422b16 100644 --- a/listener/sing_vmess/server.go +++ b/listener/sing_vmess/server.go @@ -40,10 +40,14 @@ func New(config LC.VmessServer, tunnel C.Tunnel, additions ...inbound.Addition) _listener = sl }() } - h := &sing.ListenerHandler{ + h, err := sing.NewListenerHandler(sing.ListenerConfig{ Tunnel: tunnel, Type: C.VMESS, Additions: additions, + MuxOption: config.MuxOption, + }) + if err != nil { + return nil, err } service := vmess.NewService[string](h, vmess.ServiceWithDisableHeaderProtection(), vmess.ServiceWithTimeFunc(ntp.Now)) diff --git a/listener/tuic/server.go b/listener/tuic/server.go index 7fa7b18e..5d807cbc 100644 --- a/listener/tuic/server.go +++ b/listener/tuic/server.go @@ -38,10 +38,14 @@ func New(config LC.TuicServer, tunnel C.Tunnel, additions ...inbound.Addition) ( inbound.WithSpecialRules(""), } } - h := &sing.ListenerHandler{ + h, err := sing.NewListenerHandler(sing.ListenerConfig{ Tunnel: tunnel, Type: C.TUIC, Additions: additions, + MuxOption: config.MuxOption, + }) + if err != nil { + return nil, err } cert, err := CN.ParseCert(config.Certificate, config.PrivateKey, C.Path) diff --git a/log/log.go b/log/log.go index d431dcb1..d0c9b330 100644 --- a/log/log.go +++ b/log/log.go @@ -21,6 +21,7 @@ func init() { log.SetFormatter(&log.TextFormatter{ FullTimestamp: true, TimestampFormat: "2006-01-02T15:04:05.999999999Z07:00", + ForceColors: true, }) } diff --git a/main.go b/main.go index fd1e065c..01edee9f 100644 --- a/main.go +++ b/main.go @@ -48,8 +48,8 @@ func main() { if version { fmt.Printf("Mihomo Meta %s %s %s with %s %s\n", C.Version, runtime.GOOS, runtime.GOARCH, runtime.Version(), C.BuildTime) - if len(features.TAGS) != 0 { - fmt.Printf("Use tags: %s\n", strings.Join(features.TAGS, ", ")) + if tags := features.Tags(); len(tags) != 0 { + fmt.Printf("Use tags: %s\n", strings.Join(tags, ", ")) } return diff --git a/rules/logic/logic.go b/rules/logic/logic.go index 4256a200..fde96e19 100644 --- a/rules/logic/logic.go +++ b/rules/logic/logic.go @@ -2,10 +2,10 @@ package logic import ( "fmt" + list "github.com/bahlo/generic-list-go" "regexp" "strings" - "github.com/metacubex/mihomo/common/collections" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/rules/common" ) @@ -133,7 +133,7 @@ func (logic *Logic) payloadToRule(subPayload string, parseRule ParseRuleFunc) (C } func (logic *Logic) format(payload string) ([]Range, error) { - stack := collections.NewStack() + stack := list.New[Range]() num := 0 subRanges := make([]Range, 0) for i, c := range payload { @@ -144,15 +144,16 @@ func (logic *Logic) format(payload string) ([]Range, error) { } num++ - stack.Push(sr) + stack.PushBack(sr) } else if c == ')' { if stack.Len() == 0 { return nil, fmt.Errorf("missing '('") } - sr := stack.Pop().(Range) - sr.end = i - subRanges = append(subRanges, sr) + sr := stack.Back() + stack.Remove(sr) + sr.Value.end = i + subRanges = append(subRanges, sr.Value) } } diff --git a/rules/provider/parse.go b/rules/provider/parse.go index 3a5c4fd7..a867d570 100644 --- a/rules/provider/parse.go +++ b/rules/provider/parse.go @@ -8,6 +8,7 @@ import ( "github.com/metacubex/mihomo/common/structure" "github.com/metacubex/mihomo/component/resource" C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/constant/features" P "github.com/metacubex/mihomo/constant/provider" ) @@ -62,7 +63,7 @@ func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(t case "http": if schema.Path != "" { path := C.Path.Resolve(schema.Path) - if !C.Path.IsSafePath(path) { + if !features.CMFA && !C.Path.IsSafePath(path) { return nil, fmt.Errorf("%w: %s", errSubPath, path) } vehicle = resource.NewHTTPVehicle(schema.URL, path) diff --git a/rules/provider/patch_android.go b/rules/provider/patch_android.go new file mode 100644 index 00000000..2bd5ffc8 --- /dev/null +++ b/rules/provider/patch_android.go @@ -0,0 +1,27 @@ +//go:build android && cmfa + +package provider + +import "time" + +var ( + suspended bool +) + +type UpdatableProvider interface { + UpdatedAt() time.Time +} + +func (f *ruleSetProvider) UpdatedAt() time.Time { + return f.Fetcher.UpdatedAt +} + +func (rp *ruleSetProvider) Close() error { + rp.Fetcher.Destroy() + + return nil +} + +func Suspend(s bool) { + suspended = s +} diff --git a/test/go.mod b/test/go.mod index adb42a2c..92374886 100644 --- a/test/go.mod +++ b/test/go.mod @@ -6,9 +6,9 @@ require ( github.com/docker/docker v20.10.21+incompatible github.com/docker/go-connections v0.4.0 github.com/metacubex/mihomo v0.0.0 - github.com/miekg/dns v1.1.56 + github.com/miekg/dns v1.1.57 github.com/stretchr/testify v1.8.4 - golang.org/x/net v0.17.0 + golang.org/x/net v0.18.0 ) replace github.com/metacubex/mihomo => ../ @@ -20,11 +20,14 @@ require ( github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect github.com/andybalholm/brotli v1.0.5 // indirect - github.com/cilium/ebpf v0.12.0 // indirect + github.com/bahlo/generic-list-go v0.2.0 // indirect + github.com/buger/jsonparser v1.1.1 // indirect + github.com/cilium/ebpf v0.12.3 // indirect github.com/coreos/go-iptables v0.7.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/distribution/reference v0.5.0 // indirect github.com/dlclark/regexp2 v1.10.0 // indirect - github.com/docker/distribution v2.8.2+incompatible // indirect + github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/go-units v0.4.0 // indirect github.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 // indirect github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect @@ -36,26 +39,27 @@ require ( github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gobwas/httphead v0.1.0 // indirect github.com/gobwas/pool v0.2.1 // indirect - github.com/gobwas/ws v1.3.0 // indirect + github.com/gobwas/ws v1.3.1 // indirect github.com/gofrs/uuid/v5 v5.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/btree v1.1.2 // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/hashicorp/yamux v0.1.1 // indirect - github.com/insomniacslk/dhcp v0.0.0-20230908212754-65c27093e38a // indirect + github.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c // indirect github.com/josharian/native v1.1.0 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/klauspost/compress v1.16.7 // indirect - github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mdlayher/netlink v1.7.2 // indirect github.com/mdlayher/socket v0.4.1 // indirect github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 // indirect github.com/metacubex/gvisor v0.0.0-20231001104248-0f672c3fb8d8 // indirect - github.com/metacubex/quic-go v0.39.1-0.20231019030608-fd969d66f16b // indirect - github.com/metacubex/sing-quic v0.0.0-20231008050747-a684db516966 // indirect + github.com/metacubex/quic-go v0.40.1-0.20231130135418-0c1b47cf9394 // indirect + github.com/metacubex/sing-quic v0.0.0-20231130141855-0022295e524b // indirect github.com/metacubex/sing-shadowsocks v0.2.5 // indirect github.com/metacubex/sing-shadowsocks2 v0.1.4 // indirect github.com/metacubex/sing-tun v0.1.15-0.20231103033938-170591e8d5bd // indirect @@ -74,13 +78,14 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect - github.com/puzpuzpuz/xsync/v2 v2.5.1 // indirect + github.com/puzpuzpuz/xsync/v3 v3.0.2 // indirect github.com/quic-go/qpack v0.4.0 // indirect - github.com/quic-go/qtls-go1-20 v0.3.4 // indirect + github.com/quic-go/qtls-go1-20 v0.4.1 // indirect + github.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a // indirect github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect - github.com/sagernet/sing v0.2.14 // indirect - github.com/sagernet/sing-mux v0.1.3 // indirect + github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c // indirect + github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 // indirect github.com/sagernet/sing-shadowtls v0.1.4 // indirect github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect github.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 // indirect @@ -88,7 +93,7 @@ require ( github.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f // indirect github.com/samber/lo v1.38.1 // indirect github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect - github.com/shirou/gopsutil/v3 v3.23.9 // indirect + github.com/shirou/gopsutil/v3 v3.23.10 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect @@ -98,20 +103,20 @@ require ( github.com/tklauser/numcpus v0.6.1 // indirect github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect + github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect github.com/zhangyunhao116/fastrand v0.3.0 // indirect gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect - go.etcd.io/bbolt v1.3.7 // indirect go.uber.org/mock v0.3.0 // indirect go4.org/netipx v0.0.0-20230824141953-6213f710f925 // indirect - golang.org/x/crypto v0.14.0 // indirect - golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect - golang.org/x/mod v0.13.0 // indirect - golang.org/x/sync v0.4.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/crypto v0.16.0 // indirect + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/sync v0.5.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.14.0 // indirect + golang.org/x/tools v0.15.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect lukechampine.com/blake3 v1.2.1 // indirect diff --git a/test/go.sum b/test/go.sum index c7524eff..af34b356 100644 --- a/test/go.sum +++ b/test/go.sum @@ -11,21 +11,27 @@ github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmH github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= +github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= +github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/cilium/ebpf v0.12.0 h1:oQEuIQIXgYhe1v7sYUG0P9vtJTYZLLdA6tiQmrOB1mo= -github.com/cilium/ebpf v0.12.0/go.mod h1:u9H29/Iq+8cy70YqI6p5pfADkFl3vdnV2qXDg5JL0Zo= +github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4= +github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM= github.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8= github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= +github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= -github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= +github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v20.10.21+incompatible h1:UTLdBmHk3bEY+w8qeO5KttOhy6OmXWsl/FEet9Uswog= github.com/docker/docker v20.10.21+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= @@ -56,8 +62,8 @@ github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.3.0 h1:sbeU3Y4Qzlb+MOzIe6mQGf7QR4Hkv6ZD0qhGkBFL2O0= -github.com/gobwas/ws v1.3.0/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= +github.com/gobwas/ws v1.3.1 h1:Qi34dfLMWJbiKaNbDVzM9x27nZBjmkaW6i4+Ku+pGVU= +github.com/gobwas/ws v1.3.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M= github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -69,16 +75,18 @@ github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 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= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 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-20230908212754-65c27093e38a h1:S33o3djA1nPRd+d/bf7jbbXytXuK/EoXow7+aa76grQ= -github.com/insomniacslk/dhcp v0.0.0-20230908212754-65c27093e38a/go.mod h1:zmdm3sTSDP3vOOX3CEWRkkRHtKr1DxBx+J1OQFoDQQs= +github.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c h1:PgxFEySCI41sH0mB7/2XswdXbUykQsRUGod8Rn+NubM= +github.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI= +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= @@ -88,14 +96,16 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= -github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= +github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/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= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= @@ -104,10 +114,10 @@ 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-20231001104248-0f672c3fb8d8 h1:npBvaPAT145UY8682AzpUMWpdIxJti/WPLjy7gCiYYs= github.com/metacubex/gvisor v0.0.0-20231001104248-0f672c3fb8d8/go.mod h1:ZR6Gas7P1GcADCVBc1uOrA0bLQqDDyp70+63fD/BE2c= -github.com/metacubex/quic-go v0.39.1-0.20231019030608-fd969d66f16b h1:uZ++sW8yg7Fr/Wvmmrb/V+SfxvRs0iMC+2+u2bRmO8g= -github.com/metacubex/quic-go v0.39.1-0.20231019030608-fd969d66f16b/go.mod h1:4pe6cY+nAMFU/Uxn1rfnxNIowsaJGDQ3uyy4VuiPkP4= -github.com/metacubex/sing-quic v0.0.0-20231008050747-a684db516966 h1:wbOsbU3kfD5LRuJIntJwEPmgGSQukof8CgLNypi8az8= -github.com/metacubex/sing-quic v0.0.0-20231008050747-a684db516966/go.mod h1:GU7g2AZesXItk4CspDP8Dc7eGtlA2GVDihyCwsUXRSo= +github.com/metacubex/quic-go v0.40.1-0.20231130135418-0c1b47cf9394 h1:dIT+KB2hknBCrwVAXPeY9tpzzkOZP5m40yqUteRT6/Y= +github.com/metacubex/quic-go v0.40.1-0.20231130135418-0c1b47cf9394/go.mod h1:F/t8VnA47xoia8ABlNA4InkZjssvFJ5p6E6jKdbkgAs= +github.com/metacubex/sing-quic v0.0.0-20231130141855-0022295e524b h1:7XXoEePvxfkQN9b2wB8UXU3uzb9uL8syEFF7A9VAKKQ= +github.com/metacubex/sing-quic v0.0.0-20231130141855-0022295e524b/go.mod h1:Gu5/zqZDd5G1AUtoV2yjAPWOEy7zwbU2DBUjdxJh0Kw= github.com/metacubex/sing-shadowsocks v0.2.5 h1:O2RRSHlKGEpAVG/OHJQxyHqDy8uvvdCW/oW2TDBOIhc= github.com/metacubex/sing-shadowsocks v0.2.5/go.mod h1:Xz2uW9BEYGEoA8B4XEpoxt7ERHClFCwsMAvWaruoyMo= github.com/metacubex/sing-shadowsocks2 v0.1.4 h1:OOCf8lgsVcpTOJUeaFAMzyKVebaQOBnKirDdUdBoKIE= @@ -118,8 +128,8 @@ github.com/metacubex/sing-vmess v0.1.9-0.20230921005247-a0488d7dac74 h1:FtupiyFk github.com/metacubex/sing-vmess v0.1.9-0.20230921005247-a0488d7dac74/go.mod h1:8EWBZpc+qNvf5gmvjAtMHK1/DpcWqzfcBL842K00BsM= github.com/metacubex/sing-wireguard v0.0.0-20231001110902-321836559170 h1:DBGA0hmrP4pVIwLiXUONdphjcppED+plmVaKf1oqkwk= github.com/metacubex/sing-wireguard v0.0.0-20231001110902-321836559170/go.mod h1:/VbJfbdLnANE+SKXyMk/96sTRrD4GdFLh5mkegqqFcY= -github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= -github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= +github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= +github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= @@ -150,23 +160,25 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/puzpuzpuz/xsync/v2 v2.5.1 h1:mVGYAvzDSu52+zaGyNjC+24Xw2bQi3kTr4QJ6N9pIIU= -github.com/puzpuzpuz/xsync/v2 v2.5.1/go.mod h1:gD2H2krq/w52MfPLE+Uy64TzJDVY7lP2znR9qmR35kU= +github.com/puzpuzpuz/xsync/v3 v3.0.2 h1:3yESHrRFYr6xzkz61LLkvNiPFXxJEAABanTQpKbAaew= +github.com/puzpuzpuz/xsync/v3 v3.0.2/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.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg= -github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= +github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= +github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= 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/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA= github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk= -github.com/sagernet/sing v0.2.14 h1:L3AXDh22nsOOYz2nTRU1JvpRsmzViWKI1B8TsQYG1eY= -github.com/sagernet/sing v0.2.14/go.mod h1:AhNEHu0GXrpqkuzvTwvC8+j2cQUU/dh+zLEmq4C99pg= -github.com/sagernet/sing-mux v0.1.3 h1:fAf7PZa2A55mCeh0KKM02f1k2Y4vEmxuZZ/51ahkkLA= -github.com/sagernet/sing-mux v0.1.3/go.mod h1:wGeIeiiFLx4HUM5LAg65wrNZ/X1muOimqK0PEhNbPi0= +github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c h1:uask61Pxc3nGqsOSjqnBKrwfODWRoEa80lXm04LNk0E= +github.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= +github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 h1:ncKb5tVOsCQgCsv6UpsA0jinbNb5OQ5GMPJlyQP3EHM= +github.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07/go.mod h1:u/MZf32xPG8jEKe3t+xUV67EBnKtDtCaPhsJQOQGUYU= github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k= github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= github.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as= @@ -181,8 +193,8 @@ github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM= github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg= github.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s= -github.com/shirou/gopsutil/v3 v3.23.9 h1:ZI5bWVeu2ep4/DIxB4U9okeYJ7zp/QLTO4auRb/ty/E= -github.com/shirou/gopsutil/v3 v3.23.9/go.mod h1:x/NWSb71eMcjFIO0vhyGW5nZ7oSIgVjrCnADckb85GA= +github.com/shirou/gopsutil/v3 v3.23.10 h1:/N42opWlYzegYaVkWejXWJpbzKv2JDy3mrgGzKsh9hM= +github.com/shirou/gopsutil/v3 v3.23.10/go.mod h1:JIE26kpucQi+innVlAUnIEOSBhBUkirr5b44yr55+WE= 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= @@ -216,6 +228,8 @@ github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17 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/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/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= @@ -224,8 +238,6 @@ github.com/zhangyunhao116/fastrand v0.3.0 h1:7bwe124xcckPulX6fxtr2lFdO2KQqaefdtb github.com/zhangyunhao116/fastrand v0.3.0/go.mod h1:0v5KgHho0VE6HU192HnY15de/oDS8UrbBChIFjIhBtc= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo= gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ= -go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= -go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go4.org/netipx v0.0.0-20230824141953-6213f710f925 h1:eeQDDVKFkx0g4Hyy8pHgmZaK0EqB4SD6rvKbUdN3ziQ= @@ -233,27 +245,27 @@ go4.org/netipx v0.0.0-20230824141953-6213f710f925/go.mod h1:PLyyIXexvUFg3Owu6p/W golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= 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.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= +golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -271,13 +283,13 @@ 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.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -285,8 +297,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= +golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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= diff --git a/transport/hysteria/core/client.go b/transport/hysteria/core/client.go index b556c70d..258a0005 100644 --- a/transport/hysteria/core/client.go +++ b/transport/hysteria/core/client.go @@ -135,7 +135,7 @@ func (c *Client) handleControlStream(qs quic.Connection, stream quic.Stream) (bo func (c *Client) handleMessage(qs quic.Connection) { for { - msg, err := qs.ReceiveMessage(context.Background()) + msg, err := qs.ReceiveDatagram(context.Background()) if err != nil { break } @@ -400,7 +400,7 @@ func (c *quicPktConn) WriteTo(p []byte, addr string) error { // try no frag first var msgBuf bytes.Buffer _ = struc.Pack(&msgBuf, &msg) - err = c.Session.SendMessage(msgBuf.Bytes()) + err = c.Session.SendDatagram(msgBuf.Bytes()) if err != nil { if errSize, ok := err.(quic.ErrMessageTooLarge); ok { // need to frag @@ -409,7 +409,7 @@ func (c *quicPktConn) WriteTo(p []byte, addr string) error { for _, fragMsg := range fragMsgs { msgBuf.Reset() _ = struc.Pack(&msgBuf, &fragMsg) - err = c.Session.SendMessage(msgBuf.Bytes()) + err = c.Session.SendDatagram(msgBuf.Bytes()) if err != nil { return err } diff --git a/transport/trojan/trojan.go b/transport/trojan/trojan.go index c4bd1167..09be1124 100644 --- a/transport/trojan/trojan.go +++ b/transport/trojan/trojan.go @@ -55,11 +55,12 @@ type Option struct { } type WebsocketOption struct { - Host string - Port string - Path string - Headers http.Header - V2rayHttpUpgrade bool + Host string + Port string + Path string + Headers http.Header + V2rayHttpUpgrade bool + V2rayHttpUpgradeFastOpen bool } type Trojan struct { @@ -129,14 +130,15 @@ func (t *Trojan) StreamWebsocketConn(ctx context.Context, conn net.Conn, wsOptio } return vmess.StreamWebsocketConn(ctx, conn, &vmess.WebsocketConfig{ - Host: wsOptions.Host, - Port: wsOptions.Port, - Path: wsOptions.Path, - Headers: wsOptions.Headers, - V2rayHttpUpgrade: wsOptions.V2rayHttpUpgrade, - TLS: true, - TLSConfig: tlsConfig, - ClientFingerprint: t.option.ClientFingerprint, + Host: wsOptions.Host, + Port: wsOptions.Port, + Path: wsOptions.Path, + Headers: wsOptions.Headers, + V2rayHttpUpgrade: wsOptions.V2rayHttpUpgrade, + V2rayHttpUpgradeFastOpen: wsOptions.V2rayHttpUpgradeFastOpen, + TLS: true, + TLSConfig: tlsConfig, + ClientFingerprint: t.option.ClientFingerprint, }) } diff --git a/transport/tuic/pool_client.go b/transport/tuic/pool_client.go index e492fba7..b4c319d8 100644 --- a/transport/tuic/pool_client.go +++ b/transport/tuic/pool_client.go @@ -8,12 +8,13 @@ import ( "sync" "time" - "github.com/metacubex/mihomo/common/generics/list" N "github.com/metacubex/mihomo/common/net" C "github.com/metacubex/mihomo/constant" "github.com/metacubex/mihomo/log" "github.com/metacubex/quic-go" + + list "github.com/bahlo/generic-list-go" ) type dialResult struct { diff --git a/transport/tuic/server.go b/transport/tuic/server.go index a273e462..354533aa 100644 --- a/transport/tuic/server.go +++ b/transport/tuic/server.go @@ -114,7 +114,7 @@ func (s *serverHandler) handle() { func (s *serverHandler) handleMessage() (err error) { for { var message []byte - message, err = s.quicConn.ReceiveMessage(context.Background()) + message, err = s.quicConn.ReceiveDatagram(context.Background()) if err != nil { return err } diff --git a/transport/tuic/v4/client.go b/transport/tuic/v4/client.go index 0bc8f9bb..67906959 100644 --- a/transport/tuic/v4/client.go +++ b/transport/tuic/v4/client.go @@ -11,10 +11,8 @@ import ( "sync" "sync/atomic" "time" - "unsafe" atomic2 "github.com/metacubex/mihomo/common/atomic" - "github.com/metacubex/mihomo/common/buf" N "github.com/metacubex/mihomo/common/net" "github.com/metacubex/mihomo/common/pool" C "github.com/metacubex/mihomo/constant" @@ -22,7 +20,7 @@ import ( "github.com/metacubex/mihomo/transport/tuic/common" "github.com/metacubex/quic-go" - "github.com/puzpuzpuz/xsync/v2" + "github.com/puzpuzpuz/xsync/v3" "github.com/zhangyunhao116/fastrand" ) @@ -197,7 +195,7 @@ func (t *clientImpl) handleMessage(quicConn quic.Connection) (err error) { }() for { var message []byte - message, err = quicConn.ReceiveMessage(context.Background()) + message, err = quicConn.ReceiveDatagram(context.Background()) if err != nil { return err } @@ -329,75 +327,30 @@ func (t *clientImpl) DialContextWithDialer(ctx context.Context, metadata *C.Meta } bufConn := N.NewBufferedConn(stream) - conn := &earlyConn{ExtendedConn: bufConn, bufConn: bufConn, RequestTimeout: t.RequestTimeout} - if !t.FastOpen { - err = conn.Response() - if err != nil { - return nil, err + response := func() error { + if t.RequestTimeout > 0 { + _ = bufConn.SetReadDeadline(time.Now().Add(t.RequestTimeout)) } + response, err := ReadResponse(bufConn) + if err != nil { + _ = bufConn.Close() + return err + } + if response.IsFailed() { + _ = bufConn.Close() + return errors.New("connect failed") + } + _ = bufConn.SetReadDeadline(time.Time{}) + return nil } - return conn, nil -} - -type earlyConn struct { - N.ExtendedConn // only expose standard N.ExtendedConn function to outside - bufConn *N.BufferedConn - resOnce sync.Once - resErr error - - RequestTimeout time.Duration -} - -func (conn *earlyConn) response() error { - if conn.RequestTimeout > 0 { - _ = conn.SetReadDeadline(time.Now().Add(conn.RequestTimeout)) + if t.FastOpen { + return N.NewEarlyConn(bufConn, response), nil } - response, err := ReadResponse(conn.bufConn) + err = response() if err != nil { - _ = conn.Close() - return err + return nil, err } - if response.IsFailed() { - _ = conn.Close() - return errors.New("connect failed") - } - _ = conn.SetReadDeadline(time.Time{}) - return nil -} - -func (conn *earlyConn) Response() error { - conn.resOnce.Do(func() { - conn.resErr = conn.response() - }) - return conn.resErr -} - -func (conn *earlyConn) Read(b []byte) (n int, err error) { - err = conn.Response() - if err != nil { - return 0, err - } - return conn.bufConn.Read(b) -} - -func (conn *earlyConn) ReadBuffer(buffer *buf.Buffer) (err error) { - err = conn.Response() - if err != nil { - return err - } - return conn.bufConn.ReadBuffer(buffer) -} - -func (conn *earlyConn) Upstream() any { - return conn.bufConn -} - -func (conn *earlyConn) ReaderReplaceable() bool { - return atomic.LoadUint32((*uint32)(unsafe.Pointer(&conn.resOnce))) == 1 && conn.resErr == nil -} - -func (conn *earlyConn) WriterReplaceable() bool { - return true + return bufConn, nil } func (t *clientImpl) ListenPacketWithDialer(ctx context.Context, metadata *C.Metadata, dialer C.Dialer, dialFn common.DialFunc) (net.PacketConn, error) { @@ -469,7 +422,7 @@ func NewClient(clientOption *ClientOption, udp bool, dialerRef C.Dialer) *Client ClientOption: clientOption, udp: udp, dialerRef: dialerRef, - udpInputMap: xsync.NewIntegerMapOf[uint32, net.Conn](), + udpInputMap: xsync.NewMapOf[uint32, net.Conn](), } c := &Client{ci} runtime.SetFinalizer(c, closeClient) diff --git a/transport/tuic/v4/packet.go b/transport/tuic/v4/packet.go index 5bd6504c..f282b3ed 100644 --- a/transport/tuic/v4/packet.go +++ b/transport/tuic/v4/packet.go @@ -161,7 +161,7 @@ func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err erro } default: // native data := buf.Bytes() - err = q.quicConn.SendMessage(data) + err = q.quicConn.SendDatagram(data) if err != nil { return } diff --git a/transport/tuic/v4/server.go b/transport/tuic/v4/server.go index c4e4d735..2430866f 100644 --- a/transport/tuic/v4/server.go +++ b/transport/tuic/v4/server.go @@ -17,7 +17,7 @@ import ( "github.com/gofrs/uuid/v5" "github.com/metacubex/quic-go" - "github.com/puzpuzpuz/xsync/v2" + "github.com/puzpuzpuz/xsync/v3" ) type ServerOption struct { @@ -34,7 +34,7 @@ func NewServerHandler(option *ServerOption, quicConn quic.EarlyConnection, uuid quicConn: quicConn, uuid: uuid, authCh: make(chan struct{}), - udpInputMap: xsync.NewIntegerMapOf[uint32, *atomic.Bool](), + udpInputMap: xsync.NewMapOf[uint32, *atomic.Bool](), } } diff --git a/transport/tuic/v5/client.go b/transport/tuic/v5/client.go index e37d60fc..8daa0925 100644 --- a/transport/tuic/v5/client.go +++ b/transport/tuic/v5/client.go @@ -20,7 +20,7 @@ import ( "github.com/metacubex/mihomo/transport/tuic/common" "github.com/metacubex/quic-go" - "github.com/puzpuzpuz/xsync/v2" + "github.com/puzpuzpuz/xsync/v3" "github.com/zhangyunhao116/fastrand" ) @@ -196,7 +196,7 @@ func (t *clientImpl) handleMessage(quicConn quic.Connection) (err error) { }() for { var message []byte - message, err = quicConn.ReceiveMessage(context.Background()) + message, err = quicConn.ReceiveDatagram(context.Background()) if err != nil { return err } @@ -406,7 +406,7 @@ func NewClient(clientOption *ClientOption, udp bool, dialerRef C.Dialer) *Client ClientOption: clientOption, udp: udp, dialerRef: dialerRef, - udpInputMap: *xsync.NewIntegerMapOf[uint16, net.Conn](), + udpInputMap: *xsync.NewMapOf[uint16, net.Conn](), } c := &Client{ci} runtime.SetFinalizer(c, closeClient) diff --git a/transport/tuic/v5/frag.go b/transport/tuic/v5/frag.go index 4e07a1c7..b0e1a174 100644 --- a/transport/tuic/v5/frag.go +++ b/transport/tuic/v5/frag.go @@ -4,7 +4,7 @@ import ( "bytes" "sync" - "github.com/metacubex/mihomo/common/cache" + "github.com/metacubex/mihomo/common/lru" "github.com/metacubex/quic-go" ) @@ -37,7 +37,7 @@ func fragWriteNative(quicConn quic.Connection, packet Packet, buf *bytes.Buffer, return } data := buf.Bytes() - err = quicConn.SendMessage(data) + err = quicConn.SendDatagram(data) if err != nil { return } @@ -47,7 +47,7 @@ func fragWriteNative(quicConn quic.Connection, packet Packet, buf *bytes.Buffer, } type deFragger struct { - lru *cache.LruCache[uint16, *packetBag] + lru *lru.LruCache[uint16, *packetBag] once sync.Once } @@ -63,9 +63,9 @@ func newPacketBag() *packetBag { func (d *deFragger) init() { if d.lru == nil { - d.lru = cache.New( - cache.WithAge[uint16, *packetBag](10), - cache.WithUpdateAgeOnGet[uint16, *packetBag](), + d.lru = lru.New( + lru.WithAge[uint16, *packetBag](10), + lru.WithUpdateAgeOnGet[uint16, *packetBag](), ) } } diff --git a/transport/tuic/v5/packet.go b/transport/tuic/v5/packet.go index 8ab45068..a34e6a58 100644 --- a/transport/tuic/v5/packet.go +++ b/transport/tuic/v5/packet.go @@ -184,7 +184,7 @@ func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err erro return } data := buf.Bytes() - err = q.quicConn.SendMessage(data) + err = q.quicConn.SendDatagram(data) } var tooLarge quic.ErrMessageTooLarge diff --git a/transport/tuic/v5/server.go b/transport/tuic/v5/server.go index c8170f62..8454b64c 100644 --- a/transport/tuic/v5/server.go +++ b/transport/tuic/v5/server.go @@ -16,7 +16,7 @@ import ( "github.com/gofrs/uuid/v5" "github.com/metacubex/quic-go" - "github.com/puzpuzpuz/xsync/v2" + "github.com/puzpuzpuz/xsync/v3" ) type ServerOption struct { @@ -33,7 +33,7 @@ func NewServerHandler(option *ServerOption, quicConn quic.EarlyConnection, uuid quicConn: quicConn, uuid: uuid, authCh: make(chan struct{}), - udpInputMap: xsync.NewIntegerMapOf[uint16, *serverUDPInput](), + udpInputMap: xsync.NewMapOf[uint16, *serverUDPInput](), } } diff --git a/transport/v2ray-plugin/websocket.go b/transport/v2ray-plugin/websocket.go index 1c7056d6..90ff5efe 100644 --- a/transport/v2ray-plugin/websocket.go +++ b/transport/v2ray-plugin/websocket.go @@ -12,15 +12,16 @@ import ( // Option is options of websocket obfs type Option struct { - Host string - Port string - Path string - Headers map[string]string - TLS bool - SkipCertVerify bool - Fingerprint string - Mux bool - V2rayHttpUpgrade bool + Host string + Port string + Path string + Headers map[string]string + TLS bool + SkipCertVerify bool + Fingerprint string + Mux bool + V2rayHttpUpgrade bool + V2rayHttpUpgradeFastOpen bool } // NewV2rayObfs return a HTTPObfs @@ -31,11 +32,12 @@ func NewV2rayObfs(ctx context.Context, conn net.Conn, option *Option) (net.Conn, } config := &vmess.WebsocketConfig{ - Host: option.Host, - Port: option.Port, - Path: option.Path, - V2rayHttpUpgrade: option.V2rayHttpUpgrade, - Headers: header, + Host: option.Host, + Port: option.Port, + Path: option.Path, + V2rayHttpUpgrade: option.V2rayHttpUpgrade, + V2rayHttpUpgradeFastOpen: option.V2rayHttpUpgradeFastOpen, + Headers: header, } if option.TLS { diff --git a/transport/vmess/http.go b/transport/vmess/http.go index 782e7eb2..c77a7e9d 100644 --- a/transport/vmess/http.go +++ b/transport/vmess/http.go @@ -8,7 +8,7 @@ import ( "net/http" "net/textproto" - "github.com/metacubex/mihomo/common/util" + "github.com/metacubex/mihomo/common/utils" "github.com/zhangyunhao116/fastrand" ) @@ -61,7 +61,7 @@ func (hc *httpConn) Write(b []byte) (int, error) { } u := fmt.Sprintf("http://%s%s", host, path) - req, _ := http.NewRequest(util.EmptyOr(hc.cfg.Method, http.MethodGet), u, bytes.NewBuffer(b)) + req, _ := http.NewRequest(utils.EmptyOr(hc.cfg.Method, http.MethodGet), u, bytes.NewBuffer(b)) for key, list := range hc.cfg.Headers { req.Header.Set(key, list[fastrand.Intn(len(list))]) } diff --git a/transport/vmess/websocket.go b/transport/vmess/websocket.go index b30bb8aa..43faac5a 100644 --- a/transport/vmess/websocket.go +++ b/transport/vmess/websocket.go @@ -49,16 +49,17 @@ type websocketWithEarlyDataConn struct { } type WebsocketConfig struct { - Host string - Port string - Path string - Headers http.Header - TLS bool - TLSConfig *tls.Config - MaxEarlyData int - EarlyDataHeaderName string - ClientFingerprint string - V2rayHttpUpgrade bool + Host string + Port string + Path string + Headers http.Header + TLS bool + TLSConfig *tls.Config + MaxEarlyData int + EarlyDataHeaderName string + ClientFingerprint string + V2rayHttpUpgrade bool + V2rayHttpUpgradeFastOpen bool } // Read implements net.Conn.Read() @@ -415,6 +416,22 @@ func streamWebsocketConn(ctx context.Context, conn net.Conn, c *WebsocketConfig, return nil, err } bufferedConn := N.NewBufferedConn(conn) + + if c.V2rayHttpUpgrade && c.V2rayHttpUpgradeFastOpen { + return N.NewEarlyConn(bufferedConn, func() error { + response, err := http.ReadResponse(bufferedConn.Reader(), request) + if err != nil { + return err + } + if response.StatusCode != http.StatusSwitchingProtocols || + !strings.EqualFold(response.Header.Get("Connection"), "upgrade") || + !strings.EqualFold(response.Header.Get("Upgrade"), "websocket") { + return fmt.Errorf("unexpected status: %s", response.Status) + } + return nil + }), nil + } + response, err := http.ReadResponse(bufferedConn.Reader(), request) if err != nil { return nil, err @@ -554,7 +571,14 @@ func StreamUpgradedWebsocketConn(w http.ResponseWriter, r *http.Request) (net.Co } if edBuf := decodeXray0rtt(r.Header); len(edBuf) > 0 { - conn = N.NewCachedConn(conn, edBuf) + appendOk := false + if bufConn, ok := conn.(*N.BufferedConn); ok { + appendOk = bufConn.AppendData(edBuf) + } + if !appendOk { + conn = N.NewCachedConn(conn, edBuf) + } + } return conn, nil diff --git a/tunnel/statistic/manager.go b/tunnel/statistic/manager.go index 8e962dae..08747118 100644 --- a/tunnel/statistic/manager.go +++ b/tunnel/statistic/manager.go @@ -6,7 +6,7 @@ import ( "github.com/metacubex/mihomo/common/atomic" - "github.com/puzpuzpuz/xsync/v2" + "github.com/puzpuzpuz/xsync/v3" "github.com/shirou/gopsutil/v3/process" ) @@ -14,7 +14,7 @@ var DefaultManager *Manager func init() { DefaultManager = &Manager{ - connections: xsync.NewMapOf[Tracker](), + connections: xsync.NewMapOf[string, Tracker](), uploadTemp: atomic.NewInt64(0), downloadTemp: atomic.NewInt64(0), uploadBlip: atomic.NewInt64(0), diff --git a/tunnel/statistic/patch_android.go b/tunnel/statistic/patch_android.go new file mode 100644 index 00000000..f1eee346 --- /dev/null +++ b/tunnel/statistic/patch_android.go @@ -0,0 +1,7 @@ +//go:build android && cmfa + +package statistic + +func (m *Manager) Total() (up, down int64) { + return m.uploadTotal.Load(), m.downloadTotal.Load() +} diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 596e26d7..c37b0c7d 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -18,6 +18,7 @@ import ( "github.com/metacubex/mihomo/component/resolver" "github.com/metacubex/mihomo/component/sniffer" C "github.com/metacubex/mihomo/constant" + "github.com/metacubex/mihomo/constant/features" "github.com/metacubex/mihomo/constant/provider" icontext "github.com/metacubex/mihomo/context" "github.com/metacubex/mihomo/log" @@ -620,13 +621,24 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { if attemptProcessLookup && !findProcessMode.Off() && (findProcessMode.Always() || rule.ShouldFindProcess()) { attemptProcessLookup = false - uid, path, err := P.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, int(metadata.SrcPort)) - if err != nil { - log.Debugln("[Process] find process %s: %v", metadata.String(), err) + if !features.CMFA { + // normal check for process + uid, path, err := P.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, int(metadata.SrcPort)) + if err != nil { + log.Debugln("[Process] find process %s error: %v", metadata.String(), err) + } else { + metadata.Process = filepath.Base(path) + metadata.ProcessPath = path + metadata.Uid = uid + } } else { - metadata.Process = filepath.Base(path) - metadata.ProcessPath = path - metadata.Uid = uid + // check package names + pkg, err := P.FindPackageName(metadata) + if err != nil { + log.Debugln("[Process] find process %s error: %v", metadata.String(), err) + } else { + metadata.Process = pkg + } } }