From b3d75948138403284f7e7a532807d2701dc46781 Mon Sep 17 00:00:00 2001 From: Anankke Date: Tue, 12 Apr 2022 22:06:06 -0400 Subject: [PATCH 001/250] Chore: add `none` alias to `dummy` on ShadowsocksR (#2056) --- adapter/outbound/shadowsocksr.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/adapter/outbound/shadowsocksr.go b/adapter/outbound/shadowsocksr.go index a5f07ef6..ea1c2838 100644 --- a/adapter/outbound/shadowsocksr.go +++ b/adapter/outbound/shadowsocksr.go @@ -92,6 +92,12 @@ func (ssr *ShadowSocksR) ListenPacketContext(ctx context.Context, metadata *C.Me } func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) { + // SSR protocol compatibility + // https://github.com/Dreamacro/clash/pull/2056 + if option.Cipher == "none" { + option.Cipher = "dummy" + } + addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) cipher := option.Cipher password := option.Password @@ -103,13 +109,14 @@ func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) { ivSize int key []byte ) + if option.Cipher == "dummy" { ivSize = 0 key = core.Kdf(option.Password, 16) } else { ciph, ok := coreCiph.(*core.StreamCipher) if !ok { - return nil, fmt.Errorf("%s is not dummy or a supported stream cipher in ssr", cipher) + return nil, fmt.Errorf("%s is not none or a supported stream cipher in ssr", cipher) } ivSize = ciph.IVSize() key = ciph.Key From c282d662ca78e4044bb7b2af44f37ed58bdcd9bc Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Wed, 13 Apr 2022 17:51:21 +0800 Subject: [PATCH 002/250] Fix: make golangci lint support multi GOOS --- Makefile | 6 +++++- component/process/process_windows.go | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index aed9ca4b..36d7b3a2 100644 --- a/Makefile +++ b/Makefile @@ -130,7 +130,11 @@ all-arch: $(PLATFORM_LIST) $(WINDOWS_ARCH_LIST) releases: $(gz_releases) $(zip_releases) lint: - golangci-lint run ./... + GOOS=darwin golangci-lint run ./... + GOOS=windows golangci-lint run ./... + GOOS=linux golangci-lint run ./... + GOOS=freebsd golangci-lint run ./... + GOOS=openbsd golangci-lint run ./... clean: rm $(BINDIR)/* diff --git a/component/process/process_windows.go b/component/process/process_windows.go index e2fb96ca..c51a36f6 100644 --- a/component/process/process_windows.go +++ b/component/process/process_windows.go @@ -174,7 +174,7 @@ func newSearcher(isV4, isTCP bool) *searcher { func getTransportTable(fn uintptr, family int, class int) ([]byte, error) { for size, buf := uint32(8), make([]byte, 8); ; { ptr := unsafe.Pointer(&buf[0]) - err, _, _ := syscall.Syscall6(fn, 6, uintptr(ptr), uintptr(unsafe.Pointer(&size)), 0, uintptr(family), uintptr(class), 0) + err, _, _ := syscall.SyscallN(fn, 6, uintptr(ptr), uintptr(unsafe.Pointer(&size)), 0, uintptr(family), uintptr(class), 0) switch err { case 0: @@ -209,7 +209,7 @@ func getExecPathFromPID(pid uint32) (string, error) { buf := make([]uint16, syscall.MAX_LONG_PATH) size := uint32(len(buf)) - r1, _, err := syscall.Syscall6( + r1, _, err := syscall.SyscallN( queryProcName, 4, uintptr(h), uintptr(1), From 4dfba73e5cd015f610ba6864546426e72db605a2 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Thu, 14 Apr 2022 23:27:49 +0800 Subject: [PATCH 003/250] Fix: SyscallN should not use nargs --- component/process/process_windows.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/component/process/process_windows.go b/component/process/process_windows.go index c51a36f6..26a389a0 100644 --- a/component/process/process_windows.go +++ b/component/process/process_windows.go @@ -174,7 +174,7 @@ func newSearcher(isV4, isTCP bool) *searcher { func getTransportTable(fn uintptr, family int, class int) ([]byte, error) { for size, buf := uint32(8), make([]byte, 8); ; { ptr := unsafe.Pointer(&buf[0]) - err, _, _ := syscall.SyscallN(fn, 6, uintptr(ptr), uintptr(unsafe.Pointer(&size)), 0, uintptr(family), uintptr(class), 0) + err, _, _ := syscall.SyscallN(fn, uintptr(ptr), uintptr(unsafe.Pointer(&size)), 0, uintptr(family), uintptr(class), 0) switch err { case 0: @@ -210,12 +210,12 @@ func getExecPathFromPID(pid uint32) (string, error) { buf := make([]uint16, syscall.MAX_LONG_PATH) size := uint32(len(buf)) r1, _, err := syscall.SyscallN( - queryProcName, 4, + queryProcName, uintptr(h), uintptr(1), uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&size)), - 0, 0) + ) if r1 == 0 { return "", err } From 2c9a4d276a07f1567f6ca1ae7e736a4dfa4b21da Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Thu, 14 Apr 2022 23:37:41 +0800 Subject: [PATCH 004/250] Chore: add more github action cache --- .github/workflows/docker.yml | 4 ++++ .github/workflows/release.yml | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 18590edd..19fe0d88 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -49,6 +49,8 @@ jobs: platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64 push: true tags: 'dreamacro/clash:dev,ghcr.io/dreamacro/clash:dev' + cache-from: type=gha + cache-to: type=gha,mode=max - name: Get all docker tags if: startsWith(github.ref, 'refs/tags/') @@ -74,3 +76,5 @@ jobs: platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64 push: true tags: ${{steps.tags.outputs.result}} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8226d875..08f97ba3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,9 @@ jobs: - name: Cache go module uses: actions/cache@v2 with: - path: ~/go/pkg/mod + path: | + ~/go/pkg/mod + ~/.cache/go-build key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} restore-keys: | ${{ runner.os }}-go- From e010940b614900c4c249a4d535de1bffc0632a98 Mon Sep 17 00:00:00 2001 From: Kr328 Date: Sat, 16 Apr 2022 15:31:26 +0800 Subject: [PATCH 005/250] Improve: replace bootstrap dns (#2080) --- component/resolver/defaults.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 component/resolver/defaults.go diff --git a/component/resolver/defaults.go b/component/resolver/defaults.go new file mode 100644 index 00000000..8a04bd17 --- /dev/null +++ b/component/resolver/defaults.go @@ -0,0 +1,12 @@ +//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris + +package resolver + +import _ "unsafe" + +//go:linkname defaultNS net.defaultNS +var defaultNS []string + +func init() { + defaultNS = []string{"114.114.114.114:53", "8.8.8.8:53"} +} From 6a92c6af4e565a479681f219f794fc8b5df5c9a5 Mon Sep 17 00:00:00 2001 From: Kr328 Date: Mon, 25 Apr 2022 19:50:20 +0800 Subject: [PATCH 006/250] Fix: http proxy Upgrade behavior (#2097) --- common/net/relay.go | 30 ++++++++++++++++++++ listener/http/proxy.go | 13 +++++---- listener/http/upgrade.go | 61 ++++++++++++++++++++++++++++++++++++++++ listener/http/utils.go | 12 ++++++-- tunnel/connection.go | 24 +--------------- 5 files changed, 108 insertions(+), 32 deletions(-) create mode 100644 common/net/relay.go create mode 100644 listener/http/upgrade.go diff --git a/common/net/relay.go b/common/net/relay.go new file mode 100644 index 00000000..e7157639 --- /dev/null +++ b/common/net/relay.go @@ -0,0 +1,30 @@ +package net + +import ( + "io" + "net" + "time" + + "github.com/Dreamacro/clash/common/pool" +) + +// Relay copies between left and right bidirectionally. +func Relay(leftConn, rightConn net.Conn) { + ch := make(chan error) + + go func() { + buf := pool.Get(pool.RelayBufferSize) + // Wrapping to avoid using *net.TCPConn.(ReadFrom) + // See also https://github.com/Dreamacro/clash/pull/1209 + _, err := io.CopyBuffer(WriteOnlyWriter{Writer: leftConn}, ReadOnlyReader{Reader: rightConn}, buf) + pool.Put(buf) + leftConn.SetReadDeadline(time.Now()) + ch <- err + }() + + buf := pool.Get(pool.RelayBufferSize) + io.CopyBuffer(WriteOnlyWriter{Writer: rightConn}, ReadOnlyReader{Reader: leftConn}, buf) + pool.Put(buf) + rightConn.SetReadDeadline(time.Now()) + <-ch +} diff --git a/listener/http/proxy.go b/listener/http/proxy.go index 18f1e5d4..32701e62 100644 --- a/listener/http/proxy.go +++ b/listener/http/proxy.go @@ -19,12 +19,7 @@ func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.Cache) { client := newClient(c.RemoteAddr(), in) defer client.CloseIdleConnections() - var conn *N.BufferedConn - if bufConn, ok := c.(*N.BufferedConn); ok { - conn = bufConn - } else { - conn = N.NewBufferedConn(c) - } + conn := N.NewBufferedConn(c) keepAlive := true trusted := cache == nil // disable authenticate if cache is nil @@ -66,6 +61,12 @@ func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.Cache) { request.RequestURI = "" + if isUpgradeRequest(request) { + handleUpgrade(conn, request, in) + + return // hijack connection + } + removeHopByHopHeaders(request.Header) removeExtraHTTPHostPort(request) diff --git a/listener/http/upgrade.go b/listener/http/upgrade.go new file mode 100644 index 00000000..643de541 --- /dev/null +++ b/listener/http/upgrade.go @@ -0,0 +1,61 @@ +package http + +import ( + "net" + "net/http" + "strings" + + "github.com/Dreamacro/clash/adapter/inbound" + N "github.com/Dreamacro/clash/common/net" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/transport/socks5" +) + +func isUpgradeRequest(req *http.Request) bool { + return strings.EqualFold(req.Header.Get("Connection"), "Upgrade") +} + +func handleUpgrade(conn net.Conn, request *http.Request, in chan<- C.ConnContext) { + defer conn.Close() + + removeProxyHeaders(request.Header) + removeExtraHTTPHostPort(request) + + address := request.Host + if _, _, err := net.SplitHostPort(address); err != nil { + address = net.JoinHostPort(address, "80") + } + + dstAddr := socks5.ParseAddr(address) + if dstAddr == nil { + return + } + + left, right := net.Pipe() + + in <- inbound.NewHTTP(dstAddr, conn.RemoteAddr(), right) + + bufferedLeft := N.NewBufferedConn(left) + defer bufferedLeft.Close() + + err := request.Write(bufferedLeft) + if err != nil { + return + } + + resp, err := http.ReadResponse(bufferedLeft.Reader(), request) + if err != nil { + return + } + + removeProxyHeaders(resp.Header) + + err = resp.Write(conn) + if err != nil { + return + } + + if resp.StatusCode == http.StatusSwitchingProtocols { + N.Relay(bufferedLeft, conn) + } +} diff --git a/listener/http/utils.go b/listener/http/utils.go index 74b12005..37cca791 100644 --- a/listener/http/utils.go +++ b/listener/http/utils.go @@ -8,15 +8,21 @@ import ( "strings" ) +// removeHopByHopHeaders remove Proxy-* headers +func removeProxyHeaders(header http.Header) { + header.Del("Proxy-Connection") + header.Del("Proxy-Authenticate") + header.Del("Proxy-Authorization") +} + // removeHopByHopHeaders remove hop-by-hop header func removeHopByHopHeaders(header http.Header) { // Strip hop-by-hop header based on RFC: // http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1 // https://www.mnot.net/blog/2011/07/11/what_proxies_must_do - header.Del("Proxy-Connection") - header.Del("Proxy-Authenticate") - header.Del("Proxy-Authorization") + removeProxyHeaders(header) + header.Del("TE") header.Del("Trailers") header.Del("Transfer-Encoding") diff --git a/tunnel/connection.go b/tunnel/connection.go index 45de46d7..0384e805 100644 --- a/tunnel/connection.go +++ b/tunnel/connection.go @@ -2,7 +2,6 @@ package tunnel import ( "errors" - "io" "net" "time" @@ -63,26 +62,5 @@ func handleUDPToLocal(packet C.UDPPacket, pc net.PacketConn, key string, fAddr n } func handleSocket(ctx C.ConnContext, outbound net.Conn) { - relay(ctx.Conn(), outbound) -} - -// relay copies between left and right bidirectionally. -func relay(leftConn, rightConn net.Conn) { - ch := make(chan error) - - go func() { - buf := pool.Get(pool.RelayBufferSize) - // Wrapping to avoid using *net.TCPConn.(ReadFrom) - // See also https://github.com/Dreamacro/clash/pull/1209 - _, err := io.CopyBuffer(N.WriteOnlyWriter{Writer: leftConn}, N.ReadOnlyReader{Reader: rightConn}, buf) - pool.Put(buf) - leftConn.SetReadDeadline(time.Now()) - ch <- err - }() - - buf := pool.Get(pool.RelayBufferSize) - io.CopyBuffer(N.WriteOnlyWriter{Writer: rightConn}, N.ReadOnlyReader{Reader: leftConn}, buf) - pool.Put(buf) - rightConn.SetReadDeadline(time.Now()) - <-ch + N.Relay(ctx.Conn(), outbound) } From aef4dd3fe78bea7babc7b9b4dd2a1545ee504542 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Mon, 25 Apr 2022 23:22:58 +0800 Subject: [PATCH 007/250] Fix: make log api unblocked --- hub/route/server.go | 20 ++++++++++++++++---- log/log.go | 6 +++--- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/hub/route/server.go b/hub/route/server.go index a6632071..d5d8f726 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -208,16 +208,27 @@ func getLogs(w http.ResponseWriter, r *http.Request) { render.Status(r, http.StatusOK) } + ch := make(chan log.Event, 1024) sub := log.Subscribe() defer log.UnSubscribe(sub) buf := &bytes.Buffer{} - var err error - for elm := range sub { - buf.Reset() - log := elm.(*log.Event) + + go func() { + for elm := range sub { + log := elm.(log.Event) + select { + case ch <- log: + default: + } + } + close(ch) + }() + + for log := range ch { if log.LogLevel < level { continue } + buf.Reset() if err := json.NewEncoder(buf).Encode(Log{ Type: log.Type(), @@ -226,6 +237,7 @@ func getLogs(w http.ResponseWriter, r *http.Request) { break } + var err error if wsConn == nil { _, err = w.Write(buf.Bytes()) w.(http.Flusher).Flush() diff --git a/log/log.go b/log/log.go index 3a7ea729..ba706e5f 100644 --- a/log/log.go +++ b/log/log.go @@ -74,7 +74,7 @@ func SetLevel(newLevel LogLevel) { level = newLevel } -func print(data *Event) { +func print(data Event) { if data.LogLevel < level { return } @@ -91,8 +91,8 @@ func print(data *Event) { } } -func newLog(logLevel LogLevel, format string, v ...any) *Event { - return &Event{ +func newLog(logLevel LogLevel, format string, v ...any) Event { + return Event{ LogLevel: logLevel, Payload: fmt.Sprintf(format, v...), } From 412b44a98185b2a61500628835afcbd2c115b00e Mon Sep 17 00:00:00 2001 From: Kaming Chan Date: Sat, 7 May 2022 11:00:58 +0800 Subject: [PATCH 008/250] Fix: decode nil value in slice decoder (#2102) --- common/structure/structure.go | 14 ++++++++-- common/structure/structure_test.go | 42 ++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/common/structure/structure.go b/common/structure/structure.go index 31b07024..081dd36f 100644 --- a/common/structure/structure.go +++ b/common/structure/structure.go @@ -159,9 +159,19 @@ func (d *Decoder) decodeSlice(name string, data any, val reflect.Value) error { for valSlice.Len() <= i { valSlice = reflect.Append(valSlice, reflect.Zero(valElemType)) } - currentField := valSlice.Index(i) - fieldName := fmt.Sprintf("%s[%d]", name, i) + if currentData == nil { + // in weakly type mode, null will convert to zero value + if d.option.WeaklyTypedInput { + continue + } + // in non-weakly type mode, null will convert to nil if element's zero value is nil, otherwise return an error + if elemKind := valElemType.Kind(); elemKind == reflect.Map || elemKind == reflect.Slice { + continue + } + return fmt.Errorf("'%s' can not be null", fieldName) + } + currentField := valSlice.Index(i) if err := d.decode(fieldName, currentData, currentField); err != nil { return err } diff --git a/common/structure/structure_test.go b/common/structure/structure_test.go index b3bc52a6..9f31d3d1 100644 --- a/common/structure/structure_test.go +++ b/common/structure/structure_test.go @@ -137,3 +137,45 @@ func TestStructure_Nest(t *testing.T) { assert.Nil(t, err) assert.Equal(t, s.BazOptional, goal) } + +func TestStructure_SliceNilValue(t *testing.T) { + rawMap := map[string]any{ + "foo": 1, + "bar": []any{"bar", nil}, + } + + goal := &BazSlice{ + Foo: 1, + Bar: []string{"bar", ""}, + } + + s := &BazSlice{} + err := weakTypeDecoder.Decode(rawMap, s) + assert.Nil(t, err) + assert.Equal(t, goal.Bar, s.Bar) + + s = &BazSlice{} + err = decoder.Decode(rawMap, s) + assert.NotNil(t, err) +} + +func TestStructure_SliceNilValueComplex(t *testing.T) { + rawMap := map[string]any{ + "bar": []any{map[string]any{"bar": "foo"}, nil}, + } + + s := &struct { + Bar []map[string]any `test:"bar"` + }{} + + err := decoder.Decode(rawMap, s) + assert.Nil(t, err) + assert.Nil(t, s.Bar[1]) + + ss := &struct { + Bar []Baz `test:"bar"` + }{} + + err = decoder.Decode(rawMap, ss) + assert.NotNil(t, err) +} From 5dd94c8298e2f7d0a264eb95835b9510e988441a Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Sat, 7 May 2022 21:08:15 +0800 Subject: [PATCH 009/250] Chore: update dependencies --- go.mod | 23 ++++++++++++----------- go.sum | 58 ++++++++++++++++++++++++++-------------------------------- 2 files changed, 38 insertions(+), 43 deletions(-) diff --git a/go.mod b/go.mod index 945cad1a..1bdfdb05 100644 --- a/go.mod +++ b/go.mod @@ -3,35 +3,36 @@ module github.com/Dreamacro/clash go 1.18 require ( - github.com/Dreamacro/go-shadowsocks2 v0.1.7 + github.com/Dreamacro/go-shadowsocks2 v0.1.8 github.com/go-chi/chi/v5 v5.0.7 - github.com/go-chi/cors v1.2.0 + github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.1 github.com/gofrs/uuid v4.2.0+incompatible github.com/gorilla/websocket v1.5.0 - github.com/insomniacslk/dhcp v0.0.0-20220119180841-3c283ff8b7dd - github.com/miekg/dns v1.1.47 - github.com/oschwald/geoip2-golang v1.6.1 + github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f + github.com/miekg/dns v1.1.48 + github.com/oschwald/geoip2-golang v1.7.0 github.com/sirupsen/logrus v1.8.1 github.com/stretchr/testify v1.7.1 go.etcd.io/bbolt v1.3.6 go.uber.org/atomic v1.9.0 - go.uber.org/automaxprocs v1.4.0 - golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd - golang.org/x/net v0.0.0-20220225172249-27dd8689420f + go.uber.org/automaxprocs v1.5.1 + golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 + golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 + golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 gopkg.in/yaml.v2 v2.4.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/oschwald/maxminddb-golang v1.8.0 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/oschwald/maxminddb-golang v1.9.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect golang.org/x/mod v0.4.2 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/go.sum b/go.sum index cc449383..5a3e7e8c 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,14 @@ -github.com/Dreamacro/go-shadowsocks2 v0.1.7 h1:8CtbE1HoPPMfrQZGXmlluq6dO2lL31W6WRRE8fabc4Q= -github.com/Dreamacro/go-shadowsocks2 v0.1.7/go.mod h1:8p5G4cAj5ZlXwUR+Ww63gfSikr8kvw8uw3TDwLAJpUc= +github.com/Dreamacro/go-shadowsocks2 v0.1.8 h1:Ixejp5JscEc866gAvm/l6TFd7BOBvDviKgwb1quWw3g= +github.com/Dreamacro/go-shadowsocks2 v0.1.8/go.mod h1:51y4Q6tJoCE7e8TmYXcQRqfoxPfE9Cvn79V6pB6Df7Y= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8= github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= -github.com/go-chi/cors v1.2.0 h1:tV1g1XENQ8ku4Bq3K9ub2AtgG+p16SmzeMSGTwrOKdE= -github.com/go-chi/cors v1.2.0/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= +github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= +github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8= github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns= github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= @@ -21,18 +22,16 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= -github.com/insomniacslk/dhcp v0.0.0-20220119180841-3c283ff8b7dd h1:efcJu2Vzz6DoSq245deWNzTz6l/gsqdphm3FjmI88/g= -github.com/insomniacslk/dhcp v0.0.0-20220119180841-3c283ff8b7dd/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= +github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f h1:l1QCwn715k8nYkj4Ql50rzEog3WnMdrd4YYMMwemxEo= +github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y= github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= @@ -40,14 +39,15 @@ github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcK github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= -github.com/miekg/dns v1.1.47 h1:J9bWiXbqMbnZPcY8Qi2E3EWIBsIm6MZzzJB9VRg5gL8= -github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/oschwald/geoip2-golang v1.6.1 h1:GKxT3yaWWNXSb7vj6D7eoJBns+lGYgx08QO0UcNm0YY= -github.com/oschwald/geoip2-golang v1.6.1/go.mod h1:xdvYt5xQzB8ORWFqPnqMwZpCpgNagttWdoZLlJQzg7s= -github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk= -github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis= +github.com/miekg/dns v1.1.48 h1:Ucfr7IIVyMBz4lRE8qmGUuZ4Wt3/ZGu9hmcMT3Uu4tQ= +github.com/miekg/dns v1.1.48/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/oschwald/geoip2-golang v1.7.0 h1:JW1r5AKi+vv2ujSxjKthySK3jo8w8oKWPyXsw+Qs/S8= +github.com/oschwald/geoip2-golang v1.7.0/go.mod h1:mdI/C7iK7NVMcIDDtf4bCKMJ7r0o7UwGeCo9eiitCMQ= +github.com/oschwald/maxminddb-golang v1.9.0 h1:tIk4nv6VT9OiPyrnDAfJS1s1xKDQMZOsGojab6EjC1Y= +github.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm82Cp5HyvYbt8K3zLY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -55,9 +55,7 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA= @@ -67,14 +65,13 @@ go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/automaxprocs v1.4.0 h1:CpDZl6aOlLhReez+8S3eEotD7Jx0Os++lemPlMULQP0= -go.uber.org/automaxprocs v1.4.0/go.mod h1:/mTEdr7LvHhs0v7mjdxDreTz1OG5zdZGqgOnhWiR/+Q= +go.uber.org/automaxprocs v1.5.1 h1:e1YG66Lrk73dn4qhg8WFSvhF0JuFQF0ERIp4rpuV8Qk= +go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU= 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.0.0-20210317152858-513c2a44f670/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 h1:NvGWuYG8dkDHFSKksI1P9faiVJ9rayE6l0+ouWVIDs8= +golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -87,11 +84,10 @@ golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -104,7 +100,6 @@ golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -116,8 +111,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 h1:A9i04dxx7Cribqbs8jf3FQLogkL/CV2YN7hj9KWJCkc= -golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -136,9 +131,8 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1N golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From da7ffc0da9f532057128f41ef6f74ce0c7415304 Mon Sep 17 00:00:00 2001 From: Kaming Chan Date: Fri, 13 May 2022 11:21:39 +0800 Subject: [PATCH 010/250] Fix: add length check for ssr auth_aes128_sha1 (#2129) --- transport/ssr/protocol/auth_aes128_sha1.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/transport/ssr/protocol/auth_aes128_sha1.go b/transport/ssr/protocol/auth_aes128_sha1.go index d31fb9bf..7b4da962 100644 --- a/transport/ssr/protocol/auth_aes128_sha1.go +++ b/transport/ssr/protocol/auth_aes128_sha1.go @@ -154,6 +154,9 @@ func (a *authAES128) Encode(buf *bytes.Buffer, b []byte) error { } func (a *authAES128) DecodePacket(b []byte) ([]byte, error) { + if len(b) < 4 { + return nil, errAuthAES128LengthError + } if !bytes.Equal(a.hmac(a.Key, b[:len(b)-4])[:4], b[len(b)-4:]) { return nil, errAuthAES128ChksumError } From b38444971712ee2e7ace7b0569ce2a89479a1a2d Mon Sep 17 00:00:00 2001 From: Kr328 Date: Sun, 15 May 2022 09:12:53 +0800 Subject: [PATCH 011/250] Fix: fix upgrade header detect (#2134) --- listener/http/upgrade.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/listener/http/upgrade.go b/listener/http/upgrade.go index 643de541..50cdcb0f 100644 --- a/listener/http/upgrade.go +++ b/listener/http/upgrade.go @@ -12,7 +12,15 @@ import ( ) func isUpgradeRequest(req *http.Request) bool { - return strings.EqualFold(req.Header.Get("Connection"), "Upgrade") + for _, header := range req.Header["Connection"] { + for _, elm := range strings.Split(header, ",") { + if strings.EqualFold(strings.TrimSpace(elm), "Upgrade") { + return true + } + } + } + + return false } func handleUpgrade(conn net.Conn, request *http.Request, in chan<- C.ConnContext) { From 8603ac40a126f2ce81dd9072a41123bb24d1c41c Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Tue, 17 May 2022 19:58:33 +0800 Subject: [PATCH 012/250] Chore: make linter happy --- common/pool/alloc.go | 2 +- common/singledo/singledo.go | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/common/pool/alloc.go b/common/pool/alloc.go index efc19123..25f79897 100644 --- a/common/pool/alloc.go +++ b/common/pool/alloc.go @@ -52,8 +52,8 @@ func (alloc *Allocator) Put(buf []byte) error { return errors.New("allocator Put() incorrect buffer size") } - //lint:ignore SA6002 ignore temporarily //nolint + //lint:ignore SA6002 ignore temporarily alloc.buffers[bits].Put(buf) return nil } diff --git a/common/singledo/singledo.go b/common/singledo/singledo.go index f6ff35a9..a50f1220 100644 --- a/common/singledo/singledo.go +++ b/common/singledo/singledo.go @@ -25,7 +25,6 @@ type Result struct { } // Do single.Do likes sync.singleFlight -//lint:ignore ST1008 it likes sync.singleFlight func (s *Single) Do(fn func() (any, error)) (v any, err error, shared bool) { s.mux.Lock() now := time.Now() From 09cc6b69e3b6eabcf39a53a21cb0ed0a0f8d7aa5 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Sat, 21 May 2022 17:37:06 +0800 Subject: [PATCH 013/250] Chore: cleanup test code --- test/.golangci.yaml | 16 ++++++ test/Makefile | 5 +- test/clash_test.go | 102 ++++++++++++----------------------- test/dns_test.go | 15 +++--- test/docker_test.go | 2 - test/snell_test.go | 42 ++++----------- test/ss_test.go | 45 +++++----------- test/trojan_test.go | 47 +++++++--------- test/util.go | 13 +++++ test/util_other_test.go | 6 +-- test/vmess_test.go | 116 ++++++++++++++++------------------------ 11 files changed, 164 insertions(+), 245 deletions(-) create mode 100644 test/.golangci.yaml diff --git a/test/.golangci.yaml b/test/.golangci.yaml new file mode 100644 index 00000000..cfaed770 --- /dev/null +++ b/test/.golangci.yaml @@ -0,0 +1,16 @@ +linters: + disable-all: true + enable: + - gofumpt + - govet + - gci + - staticcheck + +linters-settings: + gci: + sections: + - standard + - prefix(github.com/Dreamacro/clash) + - default + staticcheck: + go: '1.18' diff --git a/test/Makefile b/test/Makefile index 012d88d5..d238be03 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,8 +1,9 @@ lint: - golangci-lint run --disable-all -E govet -E gofumpt -E megacheck ./... + GOOS=darwin golangci-lint run ./... + GOOS=linux golangci-lint run ./... test: go test -p 1 -v ./... benchmark: - go test -benchmem -run=^$ -bench . + go test -benchmem -run=^$$ -bench . diff --git a/test/clash_test.go b/test/clash_test.go index ade2ed76..43655246 100644 --- a/test/clash_test.go +++ b/test/clash_test.go @@ -24,6 +24,7 @@ import ( "github.com/docker/docker/client" "github.com/docker/go-connections/nat" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) const ( @@ -52,13 +53,10 @@ var ( {HostPort: "10002", HostIP: "0.0.0.0"}, }, } + isDarwin = runtime.GOOS == "darwin" ) func init() { - if runtime.GOOS == "darwin" { - isDarwin = true - } - currentDir, err := os.Getwd() if err != nil { panic(err) @@ -110,6 +108,7 @@ func init() { continue } + println("pulling image:", image) imageStream, err := c.ImagePull(context.Background(), image, types.ImagePullOptions{}) if err != nil { panic(err) @@ -214,46 +213,35 @@ func testPingPongWithSocksPort(t *testing.T, port int) { pingCh, pongCh, test := newPingPongPair() go func() { l, err := Listen("tcp", ":10001") - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) defer l.Close() c, err := l.Accept() - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) buf := make([]byte, 4) - if _, err := io.ReadFull(c, buf); err != nil { - assert.FailNow(t, err.Error()) - } + _, err = io.ReadFull(c, buf) + require.NoError(t, err) pingCh <- buf - if _, err := c.Write([]byte("pong")); err != nil { - assert.FailNow(t, err.Error()) - } + _, err = c.Write([]byte("pong")) + require.NoError(t, err) }() go func() { c, err := net.Dial("tcp", fmt.Sprintf("127.0.0.1:%d", port)) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) defer c.Close() - if _, err := socks5.ClientHandshake(c, socks5.ParseAddr("127.0.0.1:10001"), socks5.CmdConnect, nil); err != nil { - assert.FailNow(t, err.Error()) - } + _, err = socks5.ClientHandshake(c, socks5.ParseAddr("127.0.0.1:10001"), socks5.CmdConnect, nil) + require.NoError(t, err) - if _, err := c.Write([]byte("ping")); err != nil { - assert.FailNow(t, err.Error()) - } + _, err = c.Write([]byte("ping")) + require.NoError(t, err) buf := make([]byte, 4) - if _, err := io.ReadFull(c, buf); err != nil { - assert.FailNow(t, err.Error()) - } + _, err = io.ReadFull(c, buf) + require.NoError(t, err) pongCh <- buf }() @@ -304,9 +292,7 @@ func testPingPongWithConn(t *testing.T, c net.Conn) error { func testPingPongWithPacketConn(t *testing.T, pc net.PacketConn) error { l, err := ListenPacket("udp", ":10001") - if err != nil { - return err - } + require.NoError(t, err) defer l.Close() rAddr := &net.UDPAddr{IP: localIP, Port: 10001} @@ -349,9 +335,7 @@ type hashPair struct { func testLargeDataWithConn(t *testing.T, c net.Conn) error { l, err := Listen("tcp", ":10001") - if err != nil { - return err - } + require.NoError(t, err) defer l.Close() times := 100 @@ -443,9 +427,7 @@ func testLargeDataWithConn(t *testing.T, c net.Conn) error { func testLargeDataWithPacketConn(t *testing.T, pc net.PacketConn) error { l, err := ListenPacket("udp", ":10001") - if err != nil { - return err - } + require.NoError(t, err) defer l.Close() rAddr := &net.UDPAddr{IP: localIP, Port: 10001} @@ -541,7 +523,7 @@ func testLargeDataWithPacketConn(t *testing.T, pc net.PacketConn) error { func testPacketConnTimeout(t *testing.T, pc net.PacketConn) error { err := pc.SetReadDeadline(time.Now().Add(time.Millisecond * 300)) - assert.NoError(t, err) + require.NoError(t, err) errCh := make(chan error, 1) go func() { @@ -564,9 +546,7 @@ func testSuit(t *testing.T, proxy C.ProxyAdapter) { DstPort: "10001", AddrType: socks5.AtypDomainName, }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) defer conn.Close() assert.NoError(t, testPingPongWithConn(t, conn)) @@ -575,9 +555,7 @@ func testSuit(t *testing.T, proxy C.ProxyAdapter) { DstPort: "10001", AddrType: socks5.AtypDomainName, }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) defer conn.Close() assert.NoError(t, testLargeDataWithConn(t, conn)) @@ -591,9 +569,7 @@ func testSuit(t *testing.T, proxy C.ProxyAdapter) { DstPort: "10001", AddrType: socks5.AtypIPv4, }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) defer pc.Close() assert.NoError(t, testPingPongWithPacketConn(t, pc)) @@ -604,9 +580,7 @@ func testSuit(t *testing.T, proxy C.ProxyAdapter) { DstPort: "10001", AddrType: socks5.AtypIPv4, }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) defer pc.Close() assert.NoError(t, testLargeDataWithPacketConn(t, pc)) @@ -617,9 +591,7 @@ func testSuit(t *testing.T, proxy C.ProxyAdapter) { DstPort: "10001", AddrType: socks5.AtypIPv4, }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) defer pc.Close() assert.NoError(t, testPacketConnTimeout(t, pc)) @@ -627,15 +599,13 @@ func testSuit(t *testing.T, proxy C.ProxyAdapter) { func benchmarkProxy(b *testing.B, proxy C.ProxyAdapter) { l, err := Listen("tcp", ":10001") - if err != nil { - assert.FailNow(b, err.Error()) - } + require.NoError(b, err) defer l.Close() go func() { c, err := l.Accept() if err != nil { - assert.FailNow(b, err.Error()) + return } defer c.Close() @@ -650,16 +620,15 @@ func benchmarkProxy(b *testing.B, proxy C.ProxyAdapter) { DstPort: "10001", AddrType: socks5.AtypDomainName, }) - if err != nil { - assert.FailNow(b, err.Error()) - } + require.NoError(b, err) + + _, err = conn.Write([]byte("skip protocol handshake")) + require.NoError(b, err) b.SetBytes(chunkSize) b.ResetTimer() for i := 0; i < b.N; i++ { - if _, err := conn.Write(chunk); err != nil { - assert.FailNow(b, err.Error()) - } + conn.Write(chunk) } } @@ -669,12 +638,11 @@ mixed-port: 10000 log-level: silent ` - if err := parseAndApply(basic); err != nil { - assert.FailNow(t, err.Error()) - } + err := parseAndApply(basic) + require.NoError(t, err) defer cleanup() - time.Sleep(waitTime) + require.True(t, TCPing(net.JoinHostPort(localIP.String(), "10000"))) testPingPongWithSocksPort(t, 10000) } diff --git a/test/dns_test.go b/test/dns_test.go index dd07bda8..c3787623 100644 --- a/test/dns_test.go +++ b/test/dns_test.go @@ -6,6 +6,7 @@ import ( "github.com/miekg/dns" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func exchange(address, domain string, tp uint16) ([]dns.RR, error) { @@ -30,18 +31,15 @@ dns: - 119.29.29.29 ` - if err := parseAndApply(basic); err != nil { - assert.FailNow(t, err.Error()) - } + err := parseAndApply(basic) + require.NoError(t, err) defer cleanup() time.Sleep(waitTime) rr, err := exchange("127.0.0.1:8553", "1.1.1.1.nip.io", dns.TypeA) assert.NoError(t, err) - if !assert.NotEmpty(t, rr) { - assert.FailNow(t, "record empty") - } + assert.NotEmptyf(t, rr, "record empty") record := rr[0].(*dns.A) assert.Equal(t, record.A.String(), "1.1.1.1") @@ -68,9 +66,8 @@ dns: - 119.29.29.29 ` - if err := parseAndApply(basic); err != nil { - assert.FailNow(t, err.Error()) - } + err := parseAndApply(basic) + require.NoError(t, err) defer cleanup() time.Sleep(waitTime) diff --git a/test/docker_test.go b/test/docker_test.go index 12c427b1..8513ddea 100644 --- a/test/docker_test.go +++ b/test/docker_test.go @@ -8,8 +8,6 @@ import ( "github.com/docker/docker/client" ) -var isDarwin = false - func startContainer(cfg *container.Config, hostCfg *container.HostConfig, name string) (string, error) { c, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) if err != nil { diff --git a/test/snell_test.go b/test/snell_test.go index 60f30708..a8618812 100644 --- a/test/snell_test.go +++ b/test/snell_test.go @@ -9,7 +9,7 @@ import ( C "github.com/Dreamacro/clash/constant" "github.com/docker/docker/api/types/container" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestClash_SnellObfsHTTP(t *testing.T) { @@ -24,9 +24,7 @@ func TestClash_SnellObfsHTTP(t *testing.T) { } id, err := startContainer(cfg, hostCfg, "snell-http") - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) t.Cleanup(func() { cleanContainer(id) @@ -41,9 +39,7 @@ func TestClash_SnellObfsHTTP(t *testing.T) { "mode": "http", }, }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) time.Sleep(waitTime) testSuit(t, proxy) @@ -61,9 +57,7 @@ func TestClash_SnellObfsTLS(t *testing.T) { } id, err := startContainer(cfg, hostCfg, "snell-tls") - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) t.Cleanup(func() { cleanContainer(id) @@ -78,9 +72,7 @@ func TestClash_SnellObfsTLS(t *testing.T) { "mode": "tls", }, }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) time.Sleep(waitTime) testSuit(t, proxy) @@ -98,9 +90,7 @@ func TestClash_Snell(t *testing.T) { } id, err := startContainer(cfg, hostCfg, "snell") - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) t.Cleanup(func() { cleanContainer(id) @@ -112,9 +102,7 @@ func TestClash_Snell(t *testing.T) { Port: 10002, Psk: "password", }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) time.Sleep(waitTime) testSuit(t, proxy) @@ -132,9 +120,7 @@ func TestClash_Snellv3(t *testing.T) { } id, err := startContainer(cfg, hostCfg, "snell") - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) t.Cleanup(func() { cleanContainer(id) @@ -148,9 +134,7 @@ func TestClash_Snellv3(t *testing.T) { UDP: true, Version: 3, }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) time.Sleep(waitTime) testSuit(t, proxy) @@ -168,9 +152,7 @@ func Benchmark_Snell(b *testing.B) { } id, err := startContainer(cfg, hostCfg, "snell-http") - if err != nil { - assert.FailNow(b, err.Error()) - } + require.NoError(b, err) b.Cleanup(func() { cleanContainer(id) @@ -185,9 +167,7 @@ func Benchmark_Snell(b *testing.B) { "mode": "http", }, }) - if err != nil { - assert.FailNow(b, err.Error()) - } + require.NoError(b, err) time.Sleep(waitTime) benchmarkProxy(b, proxy) diff --git a/test/ss_test.go b/test/ss_test.go index 17c8e0c8..b2f0b287 100644 --- a/test/ss_test.go +++ b/test/ss_test.go @@ -1,13 +1,14 @@ package main import ( + "net" "testing" "time" "github.com/Dreamacro/clash/adapter/outbound" "github.com/docker/docker/api/types/container" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestClash_Shadowsocks(t *testing.T) { @@ -22,9 +23,7 @@ func TestClash_Shadowsocks(t *testing.T) { } id, err := startContainer(cfg, hostCfg, "ss") - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) t.Cleanup(func() { cleanContainer(id) @@ -38,9 +37,7 @@ func TestClash_Shadowsocks(t *testing.T) { Cipher: "chacha20-ietf-poly1305", UDP: true, }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) time.Sleep(waitTime) testSuit(t, proxy) @@ -60,9 +57,7 @@ func TestClash_ShadowsocksObfsHTTP(t *testing.T) { } id, err := startContainer(cfg, hostCfg, "ss-obfs-http") - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) t.Cleanup(func() { cleanContainer(id) @@ -80,9 +75,7 @@ func TestClash_ShadowsocksObfsHTTP(t *testing.T) { "mode": "http", }, }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) time.Sleep(waitTime) testSuit(t, proxy) @@ -102,9 +95,7 @@ func TestClash_ShadowsocksObfsTLS(t *testing.T) { } id, err := startContainer(cfg, hostCfg, "ss-obfs-tls") - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) t.Cleanup(func() { cleanContainer(id) @@ -122,9 +113,7 @@ func TestClash_ShadowsocksObfsTLS(t *testing.T) { "mode": "tls", }, }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) time.Sleep(waitTime) testSuit(t, proxy) @@ -144,9 +133,7 @@ func TestClash_ShadowsocksV2RayPlugin(t *testing.T) { } id, err := startContainer(cfg, hostCfg, "ss-v2ray-plugin") - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) t.Cleanup(func() { cleanContainer(id) @@ -164,9 +151,7 @@ func TestClash_ShadowsocksV2RayPlugin(t *testing.T) { "mode": "websocket", }, }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) time.Sleep(waitTime) testSuit(t, proxy) @@ -184,9 +169,7 @@ func Benchmark_Shadowsocks(b *testing.B) { } id, err := startContainer(cfg, hostCfg, "ss") - if err != nil { - assert.FailNow(b, err.Error()) - } + require.NoError(b, err) b.Cleanup(func() { cleanContainer(id) @@ -200,10 +183,8 @@ func Benchmark_Shadowsocks(b *testing.B) { Cipher: "aes-256-gcm", UDP: true, }) - if err != nil { - assert.FailNow(b, err.Error()) - } + require.NoError(b, err) - time.Sleep(waitTime) + require.True(b, TCPing(net.JoinHostPort(localIP.String(), "10002"))) benchmarkProxy(b, proxy) } diff --git a/test/trojan_test.go b/test/trojan_test.go index d1ab2a00..95f8bd56 100644 --- a/test/trojan_test.go +++ b/test/trojan_test.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "net" "testing" "time" @@ -9,7 +10,7 @@ import ( C "github.com/Dreamacro/clash/constant" "github.com/docker/docker/api/types/container" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestClash_Trojan(t *testing.T) { @@ -27,9 +28,7 @@ func TestClash_Trojan(t *testing.T) { } id, err := startContainer(cfg, hostCfg, "trojan") - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) t.Cleanup(func() { cleanContainer(id) @@ -44,9 +43,7 @@ func TestClash_Trojan(t *testing.T) { SkipCertVerify: true, UDP: true, }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) time.Sleep(waitTime) testSuit(t, proxy) @@ -67,10 +64,10 @@ func TestClash_TrojanGrpc(t *testing.T) { } id, err := startContainer(cfg, hostCfg, "trojan-grpc") - if err != nil { - assert.FailNow(t, err.Error()) - } - defer cleanContainer(id) + require.NoError(t, err) + t.Cleanup(func() { + cleanContainer(id) + }) proxy, err := outbound.NewTrojan(outbound.TrojanOption{ Name: "trojan", @@ -85,9 +82,7 @@ func TestClash_TrojanGrpc(t *testing.T) { GrpcServiceName: "example", }, }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) time.Sleep(waitTime) testSuit(t, proxy) @@ -108,10 +103,10 @@ func TestClash_TrojanWebsocket(t *testing.T) { } id, err := startContainer(cfg, hostCfg, "trojan-ws") - if err != nil { - assert.FailNow(t, err.Error()) - } - defer cleanContainer(id) + require.NoError(t, err) + t.Cleanup(func() { + cleanContainer(id) + }) proxy, err := outbound.NewTrojan(outbound.TrojanOption{ Name: "trojan", @@ -123,9 +118,7 @@ func TestClash_TrojanWebsocket(t *testing.T) { UDP: true, Network: "ws", }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) time.Sleep(waitTime) testSuit(t, proxy) @@ -145,10 +138,8 @@ func Benchmark_Trojan(b *testing.B) { }, } - id, err := startContainer(cfg, hostCfg, "trojan") - if err != nil { - assert.FailNow(b, err.Error()) - } + id, err := startContainer(cfg, hostCfg, "trojan-bench") + require.NoError(b, err) b.Cleanup(func() { cleanContainer(id) @@ -163,10 +154,8 @@ func Benchmark_Trojan(b *testing.B) { SkipCertVerify: true, UDP: true, }) - if err != nil { - assert.FailNow(b, err.Error()) - } + require.NoError(b, err) - time.Sleep(waitTime) + require.True(b, TCPing(net.JoinHostPort(localIP.String(), "10002"))) benchmarkProxy(b, proxy) } diff --git a/test/util.go b/test/util.go index f3325aeb..3d0fce88 100644 --- a/test/util.go +++ b/test/util.go @@ -35,3 +35,16 @@ func ListenPacket(network, address string) (net.PacketConn, error) { } return nil, lastErr } + +func TCPing(addr string) bool { + for i := 0; i < 10; i++ { + conn, err := net.Dial("tcp", addr) + if err == nil { + conn.Close() + return true + } + time.Sleep(time.Millisecond * 500) + } + + return false +} diff --git a/test/util_other_test.go b/test/util_other_test.go index 708b6099..fc4a68af 100644 --- a/test/util_other_test.go +++ b/test/util_other_test.go @@ -4,9 +4,9 @@ package main import ( "errors" - "net" + "net/netip" ) -func defaultRouteIP() (net.IP, error) { - return nil, errors.New("not supported") +func defaultRouteIP() (netip.Addr, error) { + return netip.Addr{}, errors.New("not supported") } diff --git a/test/vmess_test.go b/test/vmess_test.go index bf66fcd8..b283ce0c 100644 --- a/test/vmess_test.go +++ b/test/vmess_test.go @@ -9,7 +9,7 @@ import ( C "github.com/Dreamacro/clash/constant" "github.com/docker/docker/api/types/container" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestClash_Vmess(t *testing.T) { @@ -25,9 +25,7 @@ func TestClash_Vmess(t *testing.T) { } id, err := startContainer(cfg, hostCfg, "vmess") - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) t.Cleanup(func() { cleanContainer(id) @@ -41,9 +39,7 @@ func TestClash_Vmess(t *testing.T) { Cipher: "auto", UDP: true, }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) time.Sleep(waitTime) testSuit(t, proxy) @@ -64,10 +60,10 @@ func TestClash_VmessTLS(t *testing.T) { } id, err := startContainer(cfg, hostCfg, "vmess-tls") - if err != nil { - assert.FailNow(t, err.Error()) - } - defer cleanContainer(id) + require.NoError(t, err) + t.Cleanup(func() { + cleanContainer(id) + }) proxy, err := outbound.NewVmess(outbound.VmessOption{ Name: "vmess", @@ -80,9 +76,7 @@ func TestClash_VmessTLS(t *testing.T) { ServerName: "example.org", UDP: true, }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) time.Sleep(waitTime) testSuit(t, proxy) @@ -103,10 +97,10 @@ func TestClash_VmessHTTP2(t *testing.T) { } id, err := startContainer(cfg, hostCfg, "vmess-http2") - if err != nil { - assert.FailNow(t, err.Error()) - } - defer cleanContainer(id) + require.NoError(t, err) + t.Cleanup(func() { + cleanContainer(id) + }) proxy, err := outbound.NewVmess(outbound.VmessOption{ Name: "vmess", @@ -124,9 +118,7 @@ func TestClash_VmessHTTP2(t *testing.T) { Path: "/test", }, }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) time.Sleep(waitTime) testSuit(t, proxy) @@ -145,10 +137,10 @@ func TestClash_VmessHTTP(t *testing.T) { } id, err := startContainer(cfg, hostCfg, "vmess-http") - if err != nil { - assert.FailNow(t, err.Error()) - } - defer cleanContainer(id) + require.NoError(t, err) + t.Cleanup(func() { + cleanContainer(id) + }) proxy, err := outbound.NewVmess(outbound.VmessOption{ Name: "vmess", @@ -176,9 +168,7 @@ func TestClash_VmessHTTP(t *testing.T) { }, }, }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) time.Sleep(waitTime) testSuit(t, proxy) @@ -197,10 +187,10 @@ func TestClash_VmessWebsocket(t *testing.T) { } id, err := startContainer(cfg, hostCfg, "vmess-ws") - if err != nil { - assert.FailNow(t, err.Error()) - } - defer cleanContainer(id) + require.NoError(t, err) + t.Cleanup(func() { + cleanContainer(id) + }) proxy, err := outbound.NewVmess(outbound.VmessOption{ Name: "vmess", @@ -211,9 +201,7 @@ func TestClash_VmessWebsocket(t *testing.T) { Network: "ws", UDP: true, }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) time.Sleep(waitTime) testSuit(t, proxy) @@ -234,10 +222,10 @@ func TestClash_VmessWebsocketTLS(t *testing.T) { } id, err := startContainer(cfg, hostCfg, "vmess-ws") - if err != nil { - assert.FailNow(t, err.Error()) - } - defer cleanContainer(id) + require.NoError(t, err) + t.Cleanup(func() { + cleanContainer(id) + }) proxy, err := outbound.NewVmess(outbound.VmessOption{ Name: "vmess", @@ -250,9 +238,7 @@ func TestClash_VmessWebsocketTLS(t *testing.T) { SkipCertVerify: true, UDP: true, }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) time.Sleep(waitTime) testSuit(t, proxy) @@ -273,10 +259,10 @@ func TestClash_VmessGrpc(t *testing.T) { } id, err := startContainer(cfg, hostCfg, "vmess-grpc") - if err != nil { - assert.FailNow(t, err.Error()) - } - defer cleanContainer(id) + require.NoError(t, err) + t.Cleanup(func() { + cleanContainer(id) + }) proxy, err := outbound.NewVmess(outbound.VmessOption{ Name: "vmess", @@ -293,9 +279,7 @@ func TestClash_VmessGrpc(t *testing.T) { GrpcServiceName: "example!", }, }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) time.Sleep(waitTime) testSuit(t, proxy) @@ -314,10 +298,10 @@ func TestClash_VmessWebsocket0RTT(t *testing.T) { } id, err := startContainer(cfg, hostCfg, "vmess-ws-0rtt") - if err != nil { - assert.FailNow(t, err.Error()) - } - defer cleanContainer(id) + require.NoError(t, err) + t.Cleanup(func() { + cleanContainer(id) + }) proxy, err := outbound.NewVmess(outbound.VmessOption{ Name: "vmess", @@ -333,9 +317,7 @@ func TestClash_VmessWebsocket0RTT(t *testing.T) { EarlyDataHeaderName: "Sec-WebSocket-Protocol", }, }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) time.Sleep(waitTime) testSuit(t, proxy) @@ -354,10 +336,10 @@ func TestClash_VmessWebsocketXray0RTT(t *testing.T) { } id, err := startContainer(cfg, hostCfg, "vmess-xray-ws-0rtt") - if err != nil { - assert.FailNow(t, err.Error()) - } - defer cleanContainer(id) + require.NoError(t, err) + t.Cleanup(func() { + cleanContainer(id) + }) proxy, err := outbound.NewVmess(outbound.VmessOption{ Name: "vmess", @@ -372,16 +354,14 @@ func TestClash_VmessWebsocketXray0RTT(t *testing.T) { Path: "/?ed=2048", }, }) - if err != nil { - assert.FailNow(t, err.Error()) - } + require.NoError(t, err) time.Sleep(waitTime) testSuit(t, proxy) } func Benchmark_Vmess(b *testing.B) { - configPath := C.Path.Resolve("vmess-aead.json") + configPath := C.Path.Resolve("vmess.json") cfg := &container.Config{ Image: ImageVmess, @@ -393,9 +373,7 @@ func Benchmark_Vmess(b *testing.B) { } id, err := startContainer(cfg, hostCfg, "vmess-aead") - if err != nil { - assert.FailNow(b, err.Error()) - } + require.NoError(b, err) b.Cleanup(func() { cleanContainer(id) @@ -410,9 +388,7 @@ func Benchmark_Vmess(b *testing.B) { AlterID: 0, UDP: true, }) - if err != nil { - assert.FailNow(b, err.Error()) - } + require.NoError(b, err) time.Sleep(waitTime) benchmarkProxy(b, proxy) From 9a31ad615192a5074a62547673b48b893416dba9 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Sat, 21 May 2022 17:46:34 +0800 Subject: [PATCH 014/250] Chore: cleanup test go.mod --- test/go.mod | 29 +- test/go.sum | 1317 +-------------------------------------------------- 2 files changed, 31 insertions(+), 1315 deletions(-) diff --git a/test/go.mod b/test/go.mod index d0a46e06..6e0867b0 100644 --- a/test/go.mod +++ b/test/go.mod @@ -3,51 +3,48 @@ module clash-test go 1.18 require ( - github.com/Dreamacro/clash v1.7.2-0.20211108085948-bd2ea2b917aa - github.com/docker/docker v20.10.13+incompatible + github.com/Dreamacro/clash v1.10.6 + github.com/docker/docker v20.10.16+incompatible github.com/docker/go-connections v0.4.0 - github.com/miekg/dns v1.1.47 + github.com/miekg/dns v1.1.49 github.com/stretchr/testify v1.7.1 - golang.org/x/net v0.0.0-20220225172249-27dd8689420f + golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 ) replace github.com/Dreamacro/clash => ../ require ( - github.com/Dreamacro/go-shadowsocks2 v0.1.7 // indirect + github.com/Dreamacro/go-shadowsocks2 v0.1.8 // indirect github.com/Microsoft/go-winio v0.5.2 // indirect - github.com/containerd/containerd v1.6.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/distribution v2.8.1+incompatible // indirect github.com/docker/go-units v0.4.0 // indirect github.com/gofrs/uuid v4.2.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/protobuf v1.5.2 // indirect - github.com/gorilla/mux v1.8.0 // indirect + github.com/google/go-cmp v0.5.6 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/insomniacslk/dhcp v0.0.0-20220119180841-3c283ff8b7dd // indirect + github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f // indirect github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect + github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect - github.com/oschwald/geoip2-golang v1.6.1 // indirect - github.com/oschwald/maxminddb-golang v1.8.0 // indirect + github.com/oschwald/geoip2-golang v1.7.0 // indirect + github.com/oschwald/maxminddb-golang v1.9.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sirupsen/logrus v1.8.1 // indirect github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect go.etcd.io/bbolt v1.3.6 // indirect go.uber.org/atomic v1.9.0 // indirect - golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect + golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 // indirect golang.org/x/mod v0.4.2 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 // indirect + golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect - google.golang.org/grpc v1.45.0 // indirect - google.golang.org/protobuf v1.27.1 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect gotest.tools/v3 v3.1.0 // indirect diff --git a/test/go.sum b/test/go.sum index 4ba11bfb..26d5e69c 100644 --- a/test/go.sum +++ b/test/go.sum @@ -1,593 +1,52 @@ -bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= -bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512/go.mod h1:FbcW6z/2VytnFDhZfumh8Ss8zxHE6qpMP5sHTRe0EaM= -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20210715213245-6c3934b029d8/go.mod h1:CzsSbkDixRphAF5hS6wbMKq0eI6ccJRb7/A0M6JBnwg= -github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest v0.11.18/go.mod h1:dSiJPy22c3u0OtOKDNttNgqpNFY/GeWa7GH/Pz56QRA= -github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= -github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/adal v0.9.13/go.mod h1:W/MM4U6nLxnIskrw4UwWzlHfGjwUS50aOsc/I3yuU8M= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/Dreamacro/go-shadowsocks2 v0.1.7 h1:8CtbE1HoPPMfrQZGXmlluq6dO2lL31W6WRRE8fabc4Q= -github.com/Dreamacro/go-shadowsocks2 v0.1.7/go.mod h1:8p5G4cAj5ZlXwUR+Ww63gfSikr8kvw8uw3TDwLAJpUc= -github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= -github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= -github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= -github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Dreamacro/go-shadowsocks2 v0.1.8 h1:Ixejp5JscEc866gAvm/l6TFd7BOBvDviKgwb1quWw3g= +github.com/Dreamacro/go-shadowsocks2 v0.1.8/go.mod h1:51y4Q6tJoCE7e8TmYXcQRqfoxPfE9Cvn79V6pB6Df7Y= github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= -github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= -github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= -github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= -github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= -github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= -github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= -github.com/Microsoft/hcsshim v0.8.20/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= -github.com/Microsoft/hcsshim v0.8.23/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= -github.com/Microsoft/hcsshim v0.9.2/go.mod h1:7pLA8lDk46WKDWlVsENo92gC0XFa8rbKfyFRBqxEbCc= -github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= -github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= -github.com/alexflint/go-filemutex v1.1.0/go.mod h1:7P4iRhttt/nUvUOrYIhcpMzv2G6CY9UnI16Z+UJqRyk= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= -github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= -github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= -github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= -github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= -github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= -github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/certifi/gocertifi v0.0.0-20191021191039-0944d244cd40/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= -github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= -github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= -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.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= -github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= -github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= -github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= -github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo= -github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= -github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= -github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= -github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= -github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= -github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= -github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= -github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= -github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= -github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= -github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= -github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= -github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8= -github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= -github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= -github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= -github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= -github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= -github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= -github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= -github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= -github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= -github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= -github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s= -github.com/containerd/containerd v1.6.1 h1:oa2uY0/0G+JX4X7hpGCYvkp9FjUancz56kSNnb1sG3o= -github.com/containerd/containerd v1.6.1/go.mod h1:1nJz5xCZPusx6jJU8Frfct988y0NpumIq9ODB0kLtoE= -github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= -github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= -github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= -github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= -github.com/containerd/continuity v0.2.2/go.mod h1:pWygW9u7LtS1o4N/Tn0FoCFDIXZ7rxcMX7HX1Dmibvk= -github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= -github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= -github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= -github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= -github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= -github.com/containerd/go-cni v1.1.0/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= -github.com/containerd/go-cni v1.1.3/go.mod h1:Rflh2EJ/++BA2/vY5ao3K6WJRR/bZKsX123aPk+kUtA= -github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= -github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= -github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= -github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= -github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= -github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= -github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= -github.com/containerd/imgcrypt v1.1.3/go.mod h1:/TPA1GIDXMzbj01yd8pIbQiLdQxed5ue1wb8bP7PQu4= -github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= -github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= -github.com/containerd/stargz-snapshotter/estargz v0.4.1/go.mod h1:x7Q9dg9QYb4+ELgxmo4gBUeJB0tl5dqH1Sdz0nJU1QM= -github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= -github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= -github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= -github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ= -github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= -github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= -github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= -github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= -github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= -github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= -github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= -github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/containernetworking/cni v1.0.1/go.mod h1:AKuhXbN5EzmD4yTNtfSsX3tPcmtrBI6QcRV0NiNt15Y= -github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= -github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= -github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNBDZcxSOplJT5ico8/FLE= -github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= -github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= -github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= -github.com/containers/ocicrypt v1.1.2/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= -github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= -github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= -github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= -github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= -github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= -github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= -github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= 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/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= -github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= -github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= -github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v20.10.13+incompatible h1:5s7uxnKZG+b8hYWlPYUi6x1Sjpq2MSt96d15eLZeHyw= -github.com/docker/docker v20.10.13+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= +github.com/docker/docker v20.10.16+incompatible h1:2Db6ZR/+FUR3hqPMwnogOPHFn405crbpxvWzKovETOQ= +github.com/docker/docker v20.10.16+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= -github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= -github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= -github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= -github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.1/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/stdr v1.2.0/go.mod h1:YkVgnZu1ZjjL7xTxrfm/LLZBfkhTqSR1ydtm6jTKKwI= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= -github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= -github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= -github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-containerregistry v0.5.1/go.mod h1:Ct15B4yir3PLOP5jsy0GNeYVaIZs/MK/Jz5any1wFW0= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= -github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= -github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/insomniacslk/dhcp v0.0.0-20220119180841-3c283ff8b7dd h1:efcJu2Vzz6DoSq245deWNzTz6l/gsqdphm3FjmI88/g= -github.com/insomniacslk/dhcp v0.0.0-20220119180841-3c283ff8b7dd/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= -github.com/intel/goresctrl v0.2.0/go.mod h1:+CZdzouYFn5EsxgqAQTEzMfwKwuc0fVdMrT9FCCAVRQ= -github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= -github.com/j-keck/arping v1.0.2/go.mod h1:aJbELhR92bSk7tp79AWM/ftfc90EfEi2bQJrbBFOsPw= -github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/joefitzgerald/rainbow-reporter v0.1.0/go.mod h1:481CNgqmVHQZzdIbN52CupLJyoVwB10FQ/IQlF1pdL8= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f h1:l1QCwn715k8nYkj4Ql50rzEog3WnMdrd4YYMMwemxEo= +github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= -github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= -github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2/go.mod h1:eD9eIE7cdwcMi9rYluz88Jz2VyhSmden33/aXg4oVIY= github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y= github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= @@ -595,641 +54,116 @@ github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcK github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/dns v1.1.47 h1:J9bWiXbqMbnZPcY8Qi2E3EWIBsIm6MZzzJB9VRg5gL8= -github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= -github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= -github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= -github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= -github.com/moby/sys/signal v0.6.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= -github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= -github.com/moby/sys/symlink v0.2.0/go.mod h1:7uZVF2dqJjG/NsClqul95CqKOBRQyYSNnJ6BMgR/gFs= -github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= -github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= +github.com/miekg/dns v1.1.49 h1:qe0mQU3Z/XpFeE+AEBo2rqaS1IPBJ3anmqZ4XiZJVG8= +github.com/miekg/dns v1.1.49/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= -github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= -github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/image-spec v1.0.2-0.20211117181255-693428a734f5/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= -github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= -github.com/opencontainers/runc v1.1.0/go.mod h1:Tj1hFw6eFWp/o33uxGf5yF2BX5yz2Z6iptFpuvbbKqc= -github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= -github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= -github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= -github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= -github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/oschwald/geoip2-golang v1.6.1 h1:GKxT3yaWWNXSb7vj6D7eoJBns+lGYgx08QO0UcNm0YY= -github.com/oschwald/geoip2-golang v1.6.1/go.mod h1:xdvYt5xQzB8ORWFqPnqMwZpCpgNagttWdoZLlJQzg7s= -github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk= -github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= -github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/oschwald/geoip2-golang v1.7.0 h1:JW1r5AKi+vv2ujSxjKthySK3jo8w8oKWPyXsw+Qs/S8= +github.com/oschwald/geoip2-golang v1.7.0/go.mod h1:mdI/C7iK7NVMcIDDtf4bCKMJ7r0o7UwGeCo9eiitCMQ= +github.com/oschwald/maxminddb-golang v1.9.0 h1:tIk4nv6VT9OiPyrnDAfJS1s1xKDQMZOsGojab6EjC1Y= +github.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm82Cp5HyvYbt8K3zLY= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= -github.com/safchain/ethtool v0.0.0-20210803160452-9aa261dae9b1/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= -github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= -github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= -github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c/go.mod h1:hzIxponao9Kjc7aWznkXaL4U4TWaDSs8zcsY4Ka08nM= github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA= github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= -github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= -github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= -github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= -github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= -github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= -github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= -github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= -go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= -go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= -go.etcd.io/etcd/pkg/v3 v3.5.0/go.mod h1:UzJGatBQ1lXChBkQF0AuAtkRQMYnHubxAEYIrC3MSsE= -go.etcd.io/etcd/raft/v3 v3.5.0/go.mod h1:UFOHSIvO/nKwd4lhkwabrTD3cqW5yVyYYf/KlD00Szc= -go.etcd.io/etcd/server/v3 v3.5.0/go.mod h1:3Ah5ruV+M+7RZr0+Y/5mNLwC+eQlni+mQmOVdCRJoS4= -go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.28.0/go.mod h1:vEhqr0m4eTc+DWxfsXoXue2GBgV2uUwVznkGIHW/e5w= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= -go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= -go.opentelemetry.io/otel v1.3.0/go.mod h1:PWIKzi6JCp7sM0k9yZ43VX+T345uNbAkDKwHVjb2PTs= -go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.3.0/go.mod h1:VpP4/RMn8bv8gNo9uK7/IMY4mtWLELsS+JIP0inH0h4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.3.0/go.mod h1:hO1KLR7jcKaDDKDkvI9dP/FIhpmna5lkqPUQdEjFAM8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.3.0/go.mod h1:keUU7UfnwWTWpJ+FWnyqmogPa82nuU5VUANFq49hlMY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.3.0/go.mod h1:QNX1aly8ehqqX1LEa6YniTU7VY9I6R3X/oPxhGdTceE= -go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= -go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= -go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= -go.opentelemetry.io/otel/sdk v1.3.0/go.mod h1:rIo4suHNhQwBIPg9axF8V9CA72Wz2mKF1teNrup8yzs= -go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= -go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= -go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= -go.opentelemetry.io/otel/trace v1.3.0/go.mod h1:c/VDhno8888bvQYmbYLqe41/Ldmr/KKunbvWM4/fEjk= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v0.11.0/go.mod h1:QpEjXPrNQzrFDZgoTo49dgHR9RYRSrg3NAKnUGl9YpQ= -go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= -golang.org/x/crypto v0.0.0-20210317152858-513c2a44f670/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38= -golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 h1:NvGWuYG8dkDHFSKksI1P9faiVJ9rayE6l0+ouWVIDs8= +golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 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.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y= +golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 h1:A9i04dxx7Cribqbs8jf3FQLogkL/CV2YN7hj9KWJCkc= -golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 h1:M73Iuj3xbbb9Uk1DYhzydthsj6oOd6l9bpuFcNoUvTs= golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190706070813-72ffa07ba3db/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 h1:BonxutuHCTL0rBDnZlKjpGIQFTjyUVTexFOdWkB6Fg0= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1237,229 +171,14 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa h1:I0YcKz0I7OAhddo7ya8kMnvprhcWM045PmkBdMO9zN0= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0 h1:NEpgUqV3Z+ZjkqMsxMg11IaDrXY4RY6CQukSGK0uI1M= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= gotest.tools/v3 v3.1.0 h1:rVV8Tcg/8jHUkPUorwjaMTtemIMVXfIPKiOqnhEhakk= gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= -k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= -k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= -k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs= -k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= -k8s.io/apimachinery v0.22.1/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0= -k8s.io/apimachinery v0.22.5/go.mod h1:xziclGKwuuJ2RM5/rSFQSYAj0zdbci3DH8kj+WvyN0U= -k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= -k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= -k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= -k8s.io/apiserver v0.22.5/go.mod h1:s2WbtgZAkTKt679sYtSudEQrTGWUSQAPe6MupLnlmaQ= -k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= -k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= -k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= -k8s.io/client-go v0.22.5/go.mod h1:cs6yf/61q2T1SdQL5Rdcjg9J1ElXSwbjSrW2vFImM4Y= -k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0= -k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= -k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= -k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= -k8s.io/component-base v0.22.5/go.mod h1:VK3I+TjuF9eaa+Ln67dKxhGar5ynVbwnGrUiNF4MqCI= -k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= -k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= -k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= -k8s.io/cri-api v0.23.1/go.mod h1:REJE3PSU0h/LOV1APBrupxrEJqnoxZC8KWzkBUHwrK4= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= -k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= -k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw= -k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= -k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210819203725-bdf08cb9a70a/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210930125809-cb0fa318a74b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.22/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= From afb3e000677e756b7b57bcff1fc6fa07e44eb4fc Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Mon, 23 May 2022 12:27:34 +0800 Subject: [PATCH 015/250] Chore: add benchmark r/w --- test/clash_test.go | 34 ++++++++++++++++++++++++++-------- test/snell_test.go | 2 +- test/ss_test.go | 2 +- test/vmess_test.go | 2 +- 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/test/clash_test.go b/test/clash_test.go index 43655246..5e4e0405 100644 --- a/test/clash_test.go +++ b/test/clash_test.go @@ -602,6 +602,10 @@ func benchmarkProxy(b *testing.B, proxy C.ProxyAdapter) { require.NoError(b, err) defer l.Close() + chunkSize := int64(16 * 1024) + chunk := make([]byte, chunkSize) + rand.Read(chunk) + go func() { c, err := l.Accept() if err != nil { @@ -609,12 +613,17 @@ func benchmarkProxy(b *testing.B, proxy C.ProxyAdapter) { } defer c.Close() + go func() { + for { + _, err := c.Write(chunk) + if err != nil { + return + } + } + }() io.Copy(io.Discard, c) }() - chunkSize := int64(16 * 1024) - chunk := make([]byte, chunkSize) - rand.Read(chunk) conn, err := proxy.DialContext(context.Background(), &C.Metadata{ Host: localIP.String(), DstPort: "10001", @@ -625,11 +634,20 @@ func benchmarkProxy(b *testing.B, proxy C.ProxyAdapter) { _, err = conn.Write([]byte("skip protocol handshake")) require.NoError(b, err) - b.SetBytes(chunkSize) - b.ResetTimer() - for i := 0; i < b.N; i++ { - conn.Write(chunk) - } + b.Run("Write", func(b *testing.B) { + b.SetBytes(chunkSize) + for i := 0; i < b.N; i++ { + conn.Write(chunk) + } + }) + + b.Run("Read", func(b *testing.B) { + b.SetBytes(chunkSize) + buf := make([]byte, chunkSize) + for i := 0; i < b.N; i++ { + conn.Read(buf) + } + }) } func TestClash_Basic(t *testing.T) { diff --git a/test/snell_test.go b/test/snell_test.go index a8618812..4c501684 100644 --- a/test/snell_test.go +++ b/test/snell_test.go @@ -151,7 +151,7 @@ func Benchmark_Snell(b *testing.B) { Binds: []string{fmt.Sprintf("%s:/config.conf", C.Path.Resolve("snell-http.conf"))}, } - id, err := startContainer(cfg, hostCfg, "snell-http") + id, err := startContainer(cfg, hostCfg, "snell-bench") require.NoError(b, err) b.Cleanup(func() { diff --git a/test/ss_test.go b/test/ss_test.go index b2f0b287..13698210 100644 --- a/test/ss_test.go +++ b/test/ss_test.go @@ -168,7 +168,7 @@ func Benchmark_Shadowsocks(b *testing.B) { PortBindings: defaultPortBindings, } - id, err := startContainer(cfg, hostCfg, "ss") + id, err := startContainer(cfg, hostCfg, "ss-bench") require.NoError(b, err) b.Cleanup(func() { diff --git a/test/vmess_test.go b/test/vmess_test.go index b283ce0c..3e8d2759 100644 --- a/test/vmess_test.go +++ b/test/vmess_test.go @@ -372,7 +372,7 @@ func Benchmark_Vmess(b *testing.B) { Binds: []string{fmt.Sprintf("%s:/etc/v2ray/config.json", configPath)}, } - id, err := startContainer(cfg, hostCfg, "vmess-aead") + id, err := startContainer(cfg, hostCfg, "vmess-bench") require.NoError(b, err) b.Cleanup(func() { From d1f0dac302463668f9d238262e659409bdca47c7 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Mon, 23 May 2022 12:30:54 +0800 Subject: [PATCH 016/250] Fix: test broken on opensource repo --- test/util_other_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/util_other_test.go b/test/util_other_test.go index fc4a68af..708b6099 100644 --- a/test/util_other_test.go +++ b/test/util_other_test.go @@ -4,9 +4,9 @@ package main import ( "errors" - "net/netip" + "net" ) -func defaultRouteIP() (netip.Addr, error) { - return netip.Addr{}, errors.New("not supported") +func defaultRouteIP() (net.IP, error) { + return nil, errors.New("not supported") } From 7f41f94fff06cf88106d3affdc4f8c518602d4a2 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Mon, 23 May 2022 12:58:18 +0800 Subject: [PATCH 017/250] Fix: benchmark read bytes --- test/clash_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/clash_test.go b/test/clash_test.go index 5e4e0405..7c9ca051 100644 --- a/test/clash_test.go +++ b/test/clash_test.go @@ -645,7 +645,7 @@ func benchmarkProxy(b *testing.B, proxy C.ProxyAdapter) { b.SetBytes(chunkSize) buf := make([]byte, chunkSize) for i := 0; i < b.N; i++ { - conn.Read(buf) + io.ReadFull(conn, buf) } }) } From 9d2fc976e2d56c1d34528847d5d3d8e232d9f656 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Thu, 26 May 2022 17:47:05 +0800 Subject: [PATCH 018/250] Chore: upgrade to yaml v3 --- adapter/provider/provider.go | 2 +- config/config.go | 2 +- go.mod | 3 +-- go.sum | 6 ++---- test/go.mod | 3 +-- test/go.sum | 6 ++---- 6 files changed, 8 insertions(+), 14 deletions(-) diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index 69f547da..e3bc4947 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -12,7 +12,7 @@ import ( C "github.com/Dreamacro/clash/constant" types "github.com/Dreamacro/clash/constant/provider" - "gopkg.in/yaml.v2" + "gopkg.in/yaml.v3" ) const ( diff --git a/config/config.go b/config/config.go index 76ed1507..8b918dd0 100644 --- a/config/config.go +++ b/config/config.go @@ -22,7 +22,7 @@ import ( R "github.com/Dreamacro/clash/rule" T "github.com/Dreamacro/clash/tunnel" - "gopkg.in/yaml.v2" + "gopkg.in/yaml.v3" ) // General config diff --git a/go.mod b/go.mod index 1bdfdb05..a19ee31c 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 - gopkg.in/yaml.v2 v2.4.0 + gopkg.in/yaml.v3 v3.0.0 ) require ( @@ -34,5 +34,4 @@ require ( golang.org/x/text v0.3.7 // indirect golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/go.sum b/go.sum index 5a3e7e8c..8060c2e4 100644 --- a/go.sum +++ b/go.sum @@ -131,8 +131,6 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1N golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/test/go.mod b/test/go.mod index 6e0867b0..ee4074fb 100644 --- a/test/go.mod +++ b/test/go.mod @@ -45,7 +45,6 @@ require ( golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + gopkg.in/yaml.v3 v3.0.0 // indirect gotest.tools/v3 v3.1.0 // indirect ) diff --git a/test/go.sum b/test/go.sum index 26d5e69c..fae4729e 100644 --- a/test/go.sum +++ b/test/go.sum @@ -174,11 +174,9 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.1.0 h1:rVV8Tcg/8jHUkPUorwjaMTtemIMVXfIPKiOqnhEhakk= gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ= From c1285adbf87b9a1745425b65404242d9bf1ec37f Mon Sep 17 00:00:00 2001 From: Hongqi Yu Date: Wed, 1 Jun 2022 10:50:54 +0800 Subject: [PATCH 019/250] Feature: can set custom interface for dns nameserver (#2126) --- config/config.go | 9 +++++++-- dns/doh.go | 9 +++++++-- dns/util.go | 2 +- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/config/config.go b/config/config.go index 8b918dd0..8b1ed89c 100644 --- a/config/config.go +++ b/config/config.go @@ -477,6 +477,10 @@ func parseNameServer(servers []string) ([]dns.NameServer, error) { return nil, fmt.Errorf("DNS NameServer[%d] format error: %s", idx, err.Error()) } + // parse with specific interface + // .e.g 10.0.0.1#en0 + interfaceName := u.Fragment + var addr, dnsNetType string switch u.Scheme { case "udp": @@ -506,8 +510,9 @@ func parseNameServer(servers []string) ([]dns.NameServer, error) { nameservers = append( nameservers, dns.NameServer{ - Net: dnsNetType, - Addr: addr, + Net: dnsNetType, + Addr: addr, + Interface: interfaceName, }, ) } diff --git a/dns/doh.go b/dns/doh.go index 94312355..99c22cb1 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -79,7 +79,7 @@ func (dc *dohClient) doRequest(req *http.Request) (msg *D.Msg, err error) { return msg, err } -func newDoHClient(url string, r *Resolver) *dohClient { +func newDoHClient(url, iface string, r *Resolver) *dohClient { return &dohClient{ url: url, transport: &http.Transport{ @@ -95,7 +95,12 @@ func newDoHClient(url string, r *Resolver) *dohClient { return nil, err } - return dialer.DialContext(ctx, "tcp", net.JoinHostPort(ip.String(), port)) + options := []dialer.Option{} + if iface != "" { + options = append(options, dialer.WithInterface(iface)) + } + + return dialer.DialContext(ctx, "tcp", net.JoinHostPort(ip.String(), port), options...) }, }, } diff --git a/dns/util.go b/dns/util.go index d11870f8..d25f5731 100644 --- a/dns/util.go +++ b/dns/util.go @@ -51,7 +51,7 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient { for _, s := range servers { switch s.Net { case "https": - ret = append(ret, newDoHClient(s.Addr, resolver)) + ret = append(ret, newDoHClient(s.Addr, s.Interface, resolver)) continue case "dhcp": ret = append(ret, newDHCPClient(s.Addr)) From 3360839fe347df4e858bc22faf74516d4d2fc4b5 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Wed, 1 Jun 2022 21:38:05 +0800 Subject: [PATCH 020/250] Chore: make CodeQL happy --- tunnel/tunnel.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 07284880..f9e13016 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -333,9 +333,9 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { if !processFound && rule.ShouldFindProcess() { processFound = true - srcPort, err := strconv.Atoi(metadata.SrcPort) + srcPort, err := strconv.ParseUint(metadata.SrcPort, 10, 16) if err == nil { - path, err := P.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, srcPort) + path, err := P.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, int(srcPort)) if err != nil { log.Debugln("[Process] find process %s: %v", metadata.String(), err) } else { From 09d49bac95c42f1b30c7791fa79bd0551c8cf9f3 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Tue, 24 May 2022 20:28:18 +0800 Subject: [PATCH 021/250] Chore: embed shadowsocks2 --- adapter/outbound/shadowsocks.go | 3 +- adapter/outbound/shadowsocksr.go | 7 +- go.mod | 1 - go.sum | 2 - test/go.mod | 1 - test/go.sum | 2 - transport/shadowsocks/README.md | 5 + transport/shadowsocks/core/cipher.go | 164 +++++++++++ transport/shadowsocks/shadowaead/cipher.go | 94 ++++++ transport/shadowsocks/shadowaead/packet.go | 95 +++++++ transport/shadowsocks/shadowaead/stream.go | 285 +++++++++++++++++++ transport/shadowsocks/shadowstream/cipher.go | 116 ++++++++ transport/shadowsocks/shadowstream/packet.go | 79 +++++ transport/shadowsocks/shadowstream/stream.go | 197 +++++++++++++ transport/snell/cipher.go | 3 +- transport/snell/pool.go | 3 +- transport/snell/snell.go | 3 +- transport/ssr/protocol/auth_chain_a.go | 3 +- transport/ssr/protocol/base.go | 3 +- 19 files changed, 1045 insertions(+), 21 deletions(-) create mode 100644 transport/shadowsocks/README.md create mode 100644 transport/shadowsocks/core/cipher.go create mode 100644 transport/shadowsocks/shadowaead/cipher.go create mode 100644 transport/shadowsocks/shadowaead/packet.go create mode 100644 transport/shadowsocks/shadowaead/stream.go create mode 100644 transport/shadowsocks/shadowstream/cipher.go create mode 100644 transport/shadowsocks/shadowstream/packet.go create mode 100644 transport/shadowsocks/shadowstream/stream.go diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index d80c7962..6ed78050 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -10,11 +10,10 @@ import ( "github.com/Dreamacro/clash/common/structure" "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/transport/shadowsocks/core" obfs "github.com/Dreamacro/clash/transport/simple-obfs" "github.com/Dreamacro/clash/transport/socks5" v2rayObfs "github.com/Dreamacro/clash/transport/v2ray-plugin" - - "github.com/Dreamacro/go-shadowsocks2/core" ) type ShadowSocks struct { diff --git a/adapter/outbound/shadowsocksr.go b/adapter/outbound/shadowsocksr.go index ea1c2838..57ef5604 100644 --- a/adapter/outbound/shadowsocksr.go +++ b/adapter/outbound/shadowsocksr.go @@ -8,12 +8,11 @@ import ( "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/transport/shadowsocks/core" + "github.com/Dreamacro/clash/transport/shadowsocks/shadowaead" + "github.com/Dreamacro/clash/transport/shadowsocks/shadowstream" "github.com/Dreamacro/clash/transport/ssr/obfs" "github.com/Dreamacro/clash/transport/ssr/protocol" - - "github.com/Dreamacro/go-shadowsocks2/core" - "github.com/Dreamacro/go-shadowsocks2/shadowaead" - "github.com/Dreamacro/go-shadowsocks2/shadowstream" ) type ShadowSocksR struct { diff --git a/go.mod b/go.mod index a19ee31c..6dfc58ac 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/Dreamacro/clash go 1.18 require ( - github.com/Dreamacro/go-shadowsocks2 v0.1.8 github.com/go-chi/chi/v5 v5.0.7 github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.1 diff --git a/go.sum b/go.sum index 8060c2e4..5ffa36ce 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/Dreamacro/go-shadowsocks2 v0.1.8 h1:Ixejp5JscEc866gAvm/l6TFd7BOBvDviKgwb1quWw3g= -github.com/Dreamacro/go-shadowsocks2 v0.1.8/go.mod h1:51y4Q6tJoCE7e8TmYXcQRqfoxPfE9Cvn79V6pB6Df7Y= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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= diff --git a/test/go.mod b/test/go.mod index ee4074fb..3af6733c 100644 --- a/test/go.mod +++ b/test/go.mod @@ -14,7 +14,6 @@ require ( replace github.com/Dreamacro/clash => ../ require ( - github.com/Dreamacro/go-shadowsocks2 v0.1.8 // indirect github.com/Microsoft/go-winio v0.5.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/distribution v2.8.1+incompatible // indirect diff --git a/test/go.sum b/test/go.sum index fae4729e..f68bd669 100644 --- a/test/go.sum +++ b/test/go.sum @@ -1,7 +1,5 @@ github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/Dreamacro/go-shadowsocks2 v0.1.8 h1:Ixejp5JscEc866gAvm/l6TFd7BOBvDviKgwb1quWw3g= -github.com/Dreamacro/go-shadowsocks2 v0.1.8/go.mod h1:51y4Q6tJoCE7e8TmYXcQRqfoxPfE9Cvn79V6pB6Df7Y= github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= diff --git a/transport/shadowsocks/README.md b/transport/shadowsocks/README.md new file mode 100644 index 00000000..c9fc815c --- /dev/null +++ b/transport/shadowsocks/README.md @@ -0,0 +1,5 @@ +## Embedded go-shadowsocks2 + +from https://github.com/Dreamacro/go-shadowsocks2 + +origin https://github.com/riobard/go-shadowsocks2 diff --git a/transport/shadowsocks/core/cipher.go b/transport/shadowsocks/core/cipher.go new file mode 100644 index 00000000..2f5acf63 --- /dev/null +++ b/transport/shadowsocks/core/cipher.go @@ -0,0 +1,164 @@ +package core + +import ( + "crypto/md5" + "errors" + "net" + "sort" + "strings" + + "github.com/Dreamacro/clash/transport/shadowsocks/shadowaead" + "github.com/Dreamacro/clash/transport/shadowsocks/shadowstream" +) + +type Cipher interface { + StreamConnCipher + PacketConnCipher +} + +type StreamConnCipher interface { + StreamConn(net.Conn) net.Conn +} + +type PacketConnCipher interface { + PacketConn(net.PacketConn) net.PacketConn +} + +// ErrCipherNotSupported occurs when a cipher is not supported (likely because of security concerns). +var ErrCipherNotSupported = errors.New("cipher not supported") + +const ( + aeadAes128Gcm = "AEAD_AES_128_GCM" + aeadAes192Gcm = "AEAD_AES_192_GCM" + aeadAes256Gcm = "AEAD_AES_256_GCM" + aeadChacha20Poly1305 = "AEAD_CHACHA20_POLY1305" + aeadXChacha20Poly1305 = "AEAD_XCHACHA20_POLY1305" +) + +// List of AEAD ciphers: key size in bytes and constructor +var aeadList = map[string]struct { + KeySize int + New func([]byte) (shadowaead.Cipher, error) +}{ + aeadAes128Gcm: {16, shadowaead.AESGCM}, + aeadAes192Gcm: {24, shadowaead.AESGCM}, + aeadAes256Gcm: {32, shadowaead.AESGCM}, + aeadChacha20Poly1305: {32, shadowaead.Chacha20Poly1305}, + aeadXChacha20Poly1305: {32, shadowaead.XChacha20Poly1305}, +} + +// List of stream ciphers: key size in bytes and constructor +var streamList = map[string]struct { + KeySize int + New func(key []byte) (shadowstream.Cipher, error) +}{ + "RC4-MD5": {16, shadowstream.RC4MD5}, + "AES-128-CTR": {16, shadowstream.AESCTR}, + "AES-192-CTR": {24, shadowstream.AESCTR}, + "AES-256-CTR": {32, shadowstream.AESCTR}, + "AES-128-CFB": {16, shadowstream.AESCFB}, + "AES-192-CFB": {24, shadowstream.AESCFB}, + "AES-256-CFB": {32, shadowstream.AESCFB}, + "CHACHA20-IETF": {32, shadowstream.Chacha20IETF}, + "XCHACHA20": {32, shadowstream.Xchacha20}, +} + +// ListCipher returns a list of available cipher names sorted alphabetically. +func ListCipher() []string { + var l []string + for k := range aeadList { + l = append(l, k) + } + for k := range streamList { + l = append(l, k) + } + sort.Strings(l) + return l +} + +// PickCipher returns a Cipher of the given name. Derive key from password if given key is empty. +func PickCipher(name string, key []byte, password string) (Cipher, error) { + name = strings.ToUpper(name) + + switch name { + case "DUMMY": + return &dummy{}, nil + case "CHACHA20-IETF-POLY1305": + name = aeadChacha20Poly1305 + case "XCHACHA20-IETF-POLY1305": + name = aeadXChacha20Poly1305 + case "AES-128-GCM": + name = aeadAes128Gcm + case "AES-192-GCM": + name = aeadAes192Gcm + case "AES-256-GCM": + name = aeadAes256Gcm + } + + if choice, ok := aeadList[name]; ok { + if len(key) == 0 { + key = Kdf(password, choice.KeySize) + } + if len(key) != choice.KeySize { + return nil, shadowaead.KeySizeError(choice.KeySize) + } + aead, err := choice.New(key) + return &AeadCipher{Cipher: aead, Key: key}, err + } + + if choice, ok := streamList[name]; ok { + if len(key) == 0 { + key = Kdf(password, choice.KeySize) + } + if len(key) != choice.KeySize { + return nil, shadowstream.KeySizeError(choice.KeySize) + } + ciph, err := choice.New(key) + return &StreamCipher{Cipher: ciph, Key: key}, err + } + + return nil, ErrCipherNotSupported +} + +type AeadCipher struct { + shadowaead.Cipher + + Key []byte +} + +func (aead *AeadCipher) StreamConn(c net.Conn) net.Conn { return shadowaead.NewConn(c, aead) } +func (aead *AeadCipher) PacketConn(c net.PacketConn) net.PacketConn { + return shadowaead.NewPacketConn(c, aead) +} + +type StreamCipher struct { + shadowstream.Cipher + + Key []byte +} + +func (ciph *StreamCipher) StreamConn(c net.Conn) net.Conn { return shadowstream.NewConn(c, ciph) } +func (ciph *StreamCipher) PacketConn(c net.PacketConn) net.PacketConn { + return shadowstream.NewPacketConn(c, ciph) +} + +// dummy cipher does not encrypt + +type dummy struct{} + +func (dummy) StreamConn(c net.Conn) net.Conn { return c } +func (dummy) PacketConn(c net.PacketConn) net.PacketConn { return c } + +// key-derivation function from original Shadowsocks +func Kdf(password string, keyLen int) []byte { + var b, prev []byte + h := md5.New() + for len(b) < keyLen { + h.Write(prev) + h.Write([]byte(password)) + b = h.Sum(b) + prev = b[len(b)-h.Size():] + h.Reset() + } + return b[:keyLen] +} diff --git a/transport/shadowsocks/shadowaead/cipher.go b/transport/shadowsocks/shadowaead/cipher.go new file mode 100644 index 00000000..3cf75749 --- /dev/null +++ b/transport/shadowsocks/shadowaead/cipher.go @@ -0,0 +1,94 @@ +package shadowaead + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/sha1" + "io" + "strconv" + + "golang.org/x/crypto/chacha20poly1305" + "golang.org/x/crypto/hkdf" +) + +type Cipher interface { + KeySize() int + SaltSize() int + Encrypter(salt []byte) (cipher.AEAD, error) + Decrypter(salt []byte) (cipher.AEAD, error) +} + +type KeySizeError int + +func (e KeySizeError) Error() string { + return "key size error: need " + strconv.Itoa(int(e)) + " bytes" +} + +func hkdfSHA1(secret, salt, info, outkey []byte) { + r := hkdf.New(sha1.New, secret, salt, info) + if _, err := io.ReadFull(r, outkey); err != nil { + panic(err) // should never happen + } +} + +type metaCipher struct { + psk []byte + makeAEAD func(key []byte) (cipher.AEAD, error) +} + +func (a *metaCipher) KeySize() int { return len(a.psk) } +func (a *metaCipher) SaltSize() int { + if ks := a.KeySize(); ks > 16 { + return ks + } + return 16 +} + +func (a *metaCipher) Encrypter(salt []byte) (cipher.AEAD, error) { + subkey := make([]byte, a.KeySize()) + hkdfSHA1(a.psk, salt, []byte("ss-subkey"), subkey) + return a.makeAEAD(subkey) +} + +func (a *metaCipher) Decrypter(salt []byte) (cipher.AEAD, error) { + subkey := make([]byte, a.KeySize()) + hkdfSHA1(a.psk, salt, []byte("ss-subkey"), subkey) + return a.makeAEAD(subkey) +} + +func aesGCM(key []byte) (cipher.AEAD, error) { + blk, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + return cipher.NewGCM(blk) +} + +// AESGCM creates a new Cipher with a pre-shared key. len(psk) must be +// one of 16, 24, or 32 to select AES-128/196/256-GCM. +func AESGCM(psk []byte) (Cipher, error) { + switch l := len(psk); l { + case 16, 24, 32: // AES 128/196/256 + default: + return nil, aes.KeySizeError(l) + } + return &metaCipher{psk: psk, makeAEAD: aesGCM}, nil +} + +// Chacha20Poly1305 creates a new Cipher with a pre-shared key. len(psk) +// must be 32. +func Chacha20Poly1305(psk []byte) (Cipher, error) { + if len(psk) != chacha20poly1305.KeySize { + return nil, KeySizeError(chacha20poly1305.KeySize) + } + return &metaCipher{psk: psk, makeAEAD: chacha20poly1305.New}, nil +} + +// XChacha20Poly1305 creates a new Cipher with a pre-shared key. len(psk) +// must be 32. +func XChacha20Poly1305(psk []byte) (Cipher, error) { + if len(psk) != chacha20poly1305.KeySize { + return nil, KeySizeError(chacha20poly1305.KeySize) + } + return &metaCipher{psk: psk, makeAEAD: chacha20poly1305.NewX}, nil +} diff --git a/transport/shadowsocks/shadowaead/packet.go b/transport/shadowsocks/shadowaead/packet.go new file mode 100644 index 00000000..7043ead7 --- /dev/null +++ b/transport/shadowsocks/shadowaead/packet.go @@ -0,0 +1,95 @@ +package shadowaead + +import ( + "crypto/rand" + "errors" + "io" + "net" + + "github.com/Dreamacro/clash/common/pool" +) + +// ErrShortPacket means that the packet is too short for a valid encrypted packet. +var ErrShortPacket = errors.New("short packet") + +var _zerononce [128]byte // read-only. 128 bytes is more than enough. + +// Pack encrypts plaintext using Cipher with a randomly generated salt and +// returns a slice of dst containing the encrypted packet and any error occurred. +// Ensure len(dst) >= ciph.SaltSize() + len(plaintext) + aead.Overhead(). +func Pack(dst, plaintext []byte, ciph Cipher) ([]byte, error) { + saltSize := ciph.SaltSize() + salt := dst[:saltSize] + if _, err := rand.Read(salt); err != nil { + return nil, err + } + aead, err := ciph.Encrypter(salt) + if err != nil { + return nil, err + } + if len(dst) < saltSize+len(plaintext)+aead.Overhead() { + return nil, io.ErrShortBuffer + } + b := aead.Seal(dst[saltSize:saltSize], _zerononce[:aead.NonceSize()], plaintext, nil) + return dst[:saltSize+len(b)], nil +} + +// Unpack decrypts pkt using Cipher and returns a slice of dst containing the decrypted payload and any error occurred. +// Ensure len(dst) >= len(pkt) - aead.SaltSize() - aead.Overhead(). +func Unpack(dst, pkt []byte, ciph Cipher) ([]byte, error) { + saltSize := ciph.SaltSize() + if len(pkt) < saltSize { + return nil, ErrShortPacket + } + salt := pkt[:saltSize] + aead, err := ciph.Decrypter(salt) + if err != nil { + return nil, err + } + if len(pkt) < saltSize+aead.Overhead() { + return nil, ErrShortPacket + } + if saltSize+len(dst)+aead.Overhead() < len(pkt) { + return nil, io.ErrShortBuffer + } + b, err := aead.Open(dst[:0], _zerononce[:aead.NonceSize()], pkt[saltSize:], nil) + return b, err +} + +type PacketConn struct { + net.PacketConn + Cipher +} + +const maxPacketSize = 64 * 1024 + +// NewPacketConn wraps a net.PacketConn with cipher +func NewPacketConn(c net.PacketConn, ciph Cipher) *PacketConn { + return &PacketConn{PacketConn: c, Cipher: ciph} +} + +// WriteTo encrypts b and write to addr using the embedded PacketConn. +func (c *PacketConn) WriteTo(b []byte, addr net.Addr) (int, error) { + buf := pool.Get(maxPacketSize) + defer pool.Put(buf) + buf, err := Pack(buf, b, c) + if err != nil { + return 0, err + } + _, err = c.PacketConn.WriteTo(buf, addr) + return len(b), err +} + +// ReadFrom reads from the embedded PacketConn and decrypts into b. +func (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) { + n, addr, err := c.PacketConn.ReadFrom(b) + if err != nil { + return n, addr, err + } + bb, err := Unpack(b[c.Cipher.SaltSize():], b[:n], c) + if err != nil { + return n, addr, err + } + copy(b, bb) + return len(bb), addr, err +} diff --git a/transport/shadowsocks/shadowaead/stream.go b/transport/shadowsocks/shadowaead/stream.go new file mode 100644 index 00000000..e92bddab --- /dev/null +++ b/transport/shadowsocks/shadowaead/stream.go @@ -0,0 +1,285 @@ +package shadowaead + +import ( + "crypto/cipher" + "crypto/rand" + "errors" + "io" + "net" + + "github.com/Dreamacro/clash/common/pool" +) + +const ( + // payloadSizeMask is the maximum size of payload in bytes. + payloadSizeMask = 0x3FFF // 16*1024 - 1 + bufSize = 17 * 1024 // >= 2+aead.Overhead()+payloadSizeMask+aead.Overhead() +) + +var ErrZeroChunk = errors.New("zero chunk") + +type Writer struct { + io.Writer + cipher.AEAD + nonce [32]byte // should be sufficient for most nonce sizes +} + +// NewWriter wraps an io.Writer with authenticated encryption. +func NewWriter(w io.Writer, aead cipher.AEAD) *Writer { return &Writer{Writer: w, AEAD: aead} } + +// Write encrypts p and writes to the embedded io.Writer. +func (w *Writer) Write(p []byte) (n int, err error) { + buf := pool.Get(bufSize) + defer pool.Put(buf) + nonce := w.nonce[:w.NonceSize()] + tag := w.Overhead() + off := 2 + tag + + // compatible with snell + if len(p) == 0 { + buf = buf[:off] + buf[0], buf[1] = byte(0), byte(0) + w.Seal(buf[:0], nonce, buf[:2], nil) + increment(nonce) + _, err = w.Writer.Write(buf) + return + } + + for nr := 0; n < len(p) && err == nil; n += nr { + nr = payloadSizeMask + if n+nr > len(p) { + nr = len(p) - n + } + buf = buf[:off+nr+tag] + buf[0], buf[1] = byte(nr>>8), byte(nr) // big-endian payload size + w.Seal(buf[:0], nonce, buf[:2], nil) + increment(nonce) + w.Seal(buf[:off], nonce, p[n:n+nr], nil) + increment(nonce) + _, err = w.Writer.Write(buf) + } + return +} + +// ReadFrom reads from the given io.Reader until EOF or error, encrypts and +// writes to the embedded io.Writer. Returns number of bytes read from r and +// any error encountered. +func (w *Writer) ReadFrom(r io.Reader) (n int64, err error) { + buf := pool.Get(bufSize) + defer pool.Put(buf) + nonce := w.nonce[:w.NonceSize()] + tag := w.Overhead() + off := 2 + tag + for { + nr, er := r.Read(buf[off : off+payloadSizeMask]) + n += int64(nr) + buf[0], buf[1] = byte(nr>>8), byte(nr) + w.Seal(buf[:0], nonce, buf[:2], nil) + increment(nonce) + w.Seal(buf[:off], nonce, buf[off:off+nr], nil) + increment(nonce) + if _, ew := w.Writer.Write(buf[:off+nr+tag]); ew != nil { + err = ew + return + } + if er != nil { + if er != io.EOF { // ignore EOF as per io.ReaderFrom contract + err = er + } + return + } + } +} + +type Reader struct { + io.Reader + cipher.AEAD + nonce [32]byte // should be sufficient for most nonce sizes + buf []byte // to be put back into bufPool + off int // offset to unconsumed part of buf +} + +// NewReader wraps an io.Reader with authenticated decryption. +func NewReader(r io.Reader, aead cipher.AEAD) *Reader { return &Reader{Reader: r, AEAD: aead} } + +// Read and decrypt a record into p. len(p) >= max payload size + AEAD overhead. +func (r *Reader) read(p []byte) (int, error) { + nonce := r.nonce[:r.NonceSize()] + tag := r.Overhead() + + // decrypt payload size + p = p[:2+tag] + if _, err := io.ReadFull(r.Reader, p); err != nil { + return 0, err + } + _, err := r.Open(p[:0], nonce, p, nil) + increment(nonce) + if err != nil { + return 0, err + } + + // decrypt payload + size := (int(p[0])<<8 + int(p[1])) & payloadSizeMask + if size == 0 { + return 0, ErrZeroChunk + } + + p = p[:size+tag] + if _, err := io.ReadFull(r.Reader, p); err != nil { + return 0, err + } + _, err = r.Open(p[:0], nonce, p, nil) + increment(nonce) + if err != nil { + return 0, err + } + return size, nil +} + +// Read reads from the embedded io.Reader, decrypts and writes to p. +func (r *Reader) Read(p []byte) (int, error) { + if r.buf == nil { + if len(p) >= payloadSizeMask+r.Overhead() { + return r.read(p) + } + b := pool.Get(bufSize) + n, err := r.read(b) + if err != nil { + return 0, err + } + r.buf = b[:n] + r.off = 0 + } + + n := copy(p, r.buf[r.off:]) + r.off += n + if r.off == len(r.buf) { + pool.Put(r.buf[:cap(r.buf)]) + r.buf = nil + } + return n, nil +} + +// WriteTo reads from the embedded io.Reader, decrypts and writes to w until +// there's no more data to write or when an error occurs. Return number of +// bytes written to w and any error encountered. +func (r *Reader) WriteTo(w io.Writer) (n int64, err error) { + if r.buf == nil { + r.buf = pool.Get(bufSize) + r.off = len(r.buf) + } + + for { + for r.off < len(r.buf) { + nw, ew := w.Write(r.buf[r.off:]) + r.off += nw + n += int64(nw) + if ew != nil { + if r.off == len(r.buf) { + pool.Put(r.buf[:cap(r.buf)]) + r.buf = nil + } + err = ew + return + } + } + + nr, er := r.read(r.buf) + if er != nil { + if er != io.EOF { + err = er + } + return + } + r.buf = r.buf[:nr] + r.off = 0 + } +} + +// increment little-endian encoded unsigned integer b. Wrap around on overflow. +func increment(b []byte) { + for i := range b { + b[i]++ + if b[i] != 0 { + return + } + } +} + +type Conn struct { + net.Conn + Cipher + r *Reader + w *Writer +} + +// NewConn wraps a stream-oriented net.Conn with cipher. +func NewConn(c net.Conn, ciph Cipher) *Conn { return &Conn{Conn: c, Cipher: ciph} } + +func (c *Conn) initReader() error { + salt := make([]byte, c.SaltSize()) + if _, err := io.ReadFull(c.Conn, salt); err != nil { + return err + } + + aead, err := c.Decrypter(salt) + if err != nil { + return err + } + + c.r = NewReader(c.Conn, aead) + return nil +} + +func (c *Conn) Read(b []byte) (int, error) { + if c.r == nil { + if err := c.initReader(); err != nil { + return 0, err + } + } + return c.r.Read(b) +} + +func (c *Conn) WriteTo(w io.Writer) (int64, error) { + if c.r == nil { + if err := c.initReader(); err != nil { + return 0, err + } + } + return c.r.WriteTo(w) +} + +func (c *Conn) initWriter() error { + salt := make([]byte, c.SaltSize()) + if _, err := rand.Read(salt); err != nil { + return err + } + aead, err := c.Encrypter(salt) + if err != nil { + return err + } + _, err = c.Conn.Write(salt) + if err != nil { + return err + } + c.w = NewWriter(c.Conn, aead) + return nil +} + +func (c *Conn) Write(b []byte) (int, error) { + if c.w == nil { + if err := c.initWriter(); err != nil { + return 0, err + } + } + return c.w.Write(b) +} + +func (c *Conn) ReadFrom(r io.Reader) (int64, error) { + if c.w == nil { + if err := c.initWriter(); err != nil { + return 0, err + } + } + return c.w.ReadFrom(r) +} diff --git a/transport/shadowsocks/shadowstream/cipher.go b/transport/shadowsocks/shadowstream/cipher.go new file mode 100644 index 00000000..dd39d03b --- /dev/null +++ b/transport/shadowsocks/shadowstream/cipher.go @@ -0,0 +1,116 @@ +package shadowstream + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/md5" + "crypto/rc4" + "strconv" + + "golang.org/x/crypto/chacha20" +) + +// Cipher generates a pair of stream ciphers for encryption and decryption. +type Cipher interface { + IVSize() int + Encrypter(iv []byte) cipher.Stream + Decrypter(iv []byte) cipher.Stream +} + +type KeySizeError int + +func (e KeySizeError) Error() string { + return "key size error: need " + strconv.Itoa(int(e)) + " bytes" +} + +// CTR mode +type ctrStream struct{ cipher.Block } + +func (b *ctrStream) IVSize() int { return b.BlockSize() } +func (b *ctrStream) Decrypter(iv []byte) cipher.Stream { return b.Encrypter(iv) } +func (b *ctrStream) Encrypter(iv []byte) cipher.Stream { return cipher.NewCTR(b, iv) } + +func AESCTR(key []byte) (Cipher, error) { + blk, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + return &ctrStream{blk}, nil +} + +// CFB mode +type cfbStream struct{ cipher.Block } + +func (b *cfbStream) IVSize() int { return b.BlockSize() } +func (b *cfbStream) Decrypter(iv []byte) cipher.Stream { return cipher.NewCFBDecrypter(b, iv) } +func (b *cfbStream) Encrypter(iv []byte) cipher.Stream { return cipher.NewCFBEncrypter(b, iv) } + +func AESCFB(key []byte) (Cipher, error) { + blk, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + return &cfbStream{blk}, nil +} + +// IETF-variant of chacha20 +type chacha20ietfkey []byte + +func (k chacha20ietfkey) IVSize() int { return chacha20.NonceSize } +func (k chacha20ietfkey) Decrypter(iv []byte) cipher.Stream { return k.Encrypter(iv) } +func (k chacha20ietfkey) Encrypter(iv []byte) cipher.Stream { + ciph, err := chacha20.NewUnauthenticatedCipher(k, iv) + if err != nil { + panic(err) // should never happen + } + return ciph +} + +func Chacha20IETF(key []byte) (Cipher, error) { + if len(key) != chacha20.KeySize { + return nil, KeySizeError(chacha20.KeySize) + } + return chacha20ietfkey(key), nil +} + +type xchacha20key []byte + +func (k xchacha20key) IVSize() int { return chacha20.NonceSizeX } +func (k xchacha20key) Decrypter(iv []byte) cipher.Stream { return k.Encrypter(iv) } +func (k xchacha20key) Encrypter(iv []byte) cipher.Stream { + ciph, err := chacha20.NewUnauthenticatedCipher(k, iv) + if err != nil { + panic(err) // should never happen + } + return ciph +} + +func Xchacha20(key []byte) (Cipher, error) { + if len(key) != chacha20.KeySize { + return nil, KeySizeError(chacha20.KeySize) + } + return xchacha20key(key), nil +} + +type rc4Md5Key []byte + +func (k rc4Md5Key) IVSize() int { + return 16 +} + +func (k rc4Md5Key) Encrypter(iv []byte) cipher.Stream { + h := md5.New() + h.Write([]byte(k)) + h.Write(iv) + rc4key := h.Sum(nil) + c, _ := rc4.NewCipher(rc4key) + return c +} + +func (k rc4Md5Key) Decrypter(iv []byte) cipher.Stream { + return k.Encrypter(iv) +} + +func RC4MD5(key []byte) (Cipher, error) { + return rc4Md5Key(key), nil +} diff --git a/transport/shadowsocks/shadowstream/packet.go b/transport/shadowsocks/shadowstream/packet.go new file mode 100644 index 00000000..0b46dea1 --- /dev/null +++ b/transport/shadowsocks/shadowstream/packet.go @@ -0,0 +1,79 @@ +package shadowstream + +import ( + "crypto/rand" + "errors" + "io" + "net" + + "github.com/Dreamacro/clash/common/pool" +) + +// ErrShortPacket means the packet is too short to be a valid encrypted packet. +var ErrShortPacket = errors.New("short packet") + +// Pack encrypts plaintext using stream cipher s and a random IV. +// Returns a slice of dst containing random IV and ciphertext. +// Ensure len(dst) >= s.IVSize() + len(plaintext). +func Pack(dst, plaintext []byte, s Cipher) ([]byte, error) { + if len(dst) < s.IVSize()+len(plaintext) { + return nil, io.ErrShortBuffer + } + iv := dst[:s.IVSize()] + _, err := rand.Read(iv) + if err != nil { + return nil, err + } + s.Encrypter(iv).XORKeyStream(dst[len(iv):], plaintext) + return dst[:len(iv)+len(plaintext)], nil +} + +// Unpack decrypts pkt using stream cipher s. +// Returns a slice of dst containing decrypted plaintext. +func Unpack(dst, pkt []byte, s Cipher) ([]byte, error) { + if len(pkt) < s.IVSize() { + return nil, ErrShortPacket + } + if len(dst) < len(pkt)-s.IVSize() { + return nil, io.ErrShortBuffer + } + iv := pkt[:s.IVSize()] + s.Decrypter(iv).XORKeyStream(dst, pkt[len(iv):]) + return dst[:len(pkt)-len(iv)], nil +} + +type PacketConn struct { + net.PacketConn + Cipher +} + +// NewPacketConn wraps a net.PacketConn with stream cipher encryption/decryption. +func NewPacketConn(c net.PacketConn, ciph Cipher) *PacketConn { + return &PacketConn{PacketConn: c, Cipher: ciph} +} + +const maxPacketSize = 64 * 1024 + +func (c *PacketConn) WriteTo(b []byte, addr net.Addr) (int, error) { + buf := pool.Get(maxPacketSize) + defer pool.Put(buf) + buf, err := Pack(buf, b, c.Cipher) + if err != nil { + return 0, err + } + _, err = c.PacketConn.WriteTo(buf, addr) + return len(b), err +} + +func (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) { + n, addr, err := c.PacketConn.ReadFrom(b) + if err != nil { + return n, addr, err + } + bb, err := Unpack(b[c.IVSize():], b[:n], c.Cipher) + if err != nil { + return n, addr, err + } + copy(b, bb) + return len(bb), addr, err +} diff --git a/transport/shadowsocks/shadowstream/stream.go b/transport/shadowsocks/shadowstream/stream.go new file mode 100644 index 00000000..6c4b0f6d --- /dev/null +++ b/transport/shadowsocks/shadowstream/stream.go @@ -0,0 +1,197 @@ +package shadowstream + +import ( + "crypto/cipher" + "crypto/rand" + "io" + "net" +) + +const bufSize = 2048 + +type Writer struct { + io.Writer + cipher.Stream + buf [bufSize]byte +} + +// NewWriter wraps an io.Writer with stream cipher encryption. +func NewWriter(w io.Writer, s cipher.Stream) *Writer { return &Writer{Writer: w, Stream: s} } + +func (w *Writer) Write(p []byte) (n int, err error) { + buf := w.buf[:] + for nw := 0; n < len(p) && err == nil; n += nw { + end := n + len(buf) + if end > len(p) { + end = len(p) + } + w.XORKeyStream(buf, p[n:end]) + nw, err = w.Writer.Write(buf[:end-n]) + } + return +} + +func (w *Writer) ReadFrom(r io.Reader) (n int64, err error) { + buf := w.buf[:] + for { + nr, er := r.Read(buf) + n += int64(nr) + b := buf[:nr] + w.XORKeyStream(b, b) + if _, err = w.Writer.Write(b); err != nil { + return + } + if er != nil { + if er != io.EOF { // ignore EOF as per io.ReaderFrom contract + err = er + } + return + } + } +} + +type Reader struct { + io.Reader + cipher.Stream + buf [bufSize]byte +} + +// NewReader wraps an io.Reader with stream cipher decryption. +func NewReader(r io.Reader, s cipher.Stream) *Reader { return &Reader{Reader: r, Stream: s} } + +func (r *Reader) Read(p []byte) (n int, err error) { + n, err = r.Reader.Read(p) + if err != nil { + return 0, err + } + r.XORKeyStream(p, p[:n]) + return +} + +func (r *Reader) WriteTo(w io.Writer) (n int64, err error) { + buf := r.buf[:] + for { + nr, er := r.Reader.Read(buf) + if nr > 0 { + r.XORKeyStream(buf, buf[:nr]) + nw, ew := w.Write(buf[:nr]) + n += int64(nw) + if ew != nil { + err = ew + return + } + } + if er != nil { + if er != io.EOF { // ignore EOF as per io.Copy contract (using src.WriteTo shortcut) + err = er + } + return + } + } +} + +// A Conn represents a Shadowsocks connection. It implements the net.Conn interface. +type Conn struct { + net.Conn + Cipher + r *Reader + w *Writer + readIV []byte + writeIV []byte +} + +// NewConn wraps a stream-oriented net.Conn with stream cipher encryption/decryption. +func NewConn(c net.Conn, ciph Cipher) *Conn { return &Conn{Conn: c, Cipher: ciph} } + +func (c *Conn) initReader() error { + if c.r == nil { + iv, err := c.ObtainReadIV() + if err != nil { + return err + } + c.r = NewReader(c.Conn, c.Decrypter(iv)) + } + return nil +} + +func (c *Conn) Read(b []byte) (int, error) { + if c.r == nil { + if err := c.initReader(); err != nil { + return 0, err + } + } + return c.r.Read(b) +} + +func (c *Conn) WriteTo(w io.Writer) (int64, error) { + if c.r == nil { + if err := c.initReader(); err != nil { + return 0, err + } + } + return c.r.WriteTo(w) +} + +func (c *Conn) initWriter() error { + if c.w == nil { + iv, err := c.ObtainWriteIV() + if err != nil { + return err + } + if _, err := c.Conn.Write(iv); err != nil { + return err + } + c.w = NewWriter(c.Conn, c.Encrypter(iv)) + } + return nil +} + +func (c *Conn) Write(b []byte) (int, error) { + if c.w == nil { + if err := c.initWriter(); err != nil { + return 0, err + } + } + return c.w.Write(b) +} + +func (c *Conn) ReadFrom(r io.Reader) (int64, error) { + if c.w == nil { + if err := c.initWriter(); err != nil { + return 0, err + } + } + return c.w.ReadFrom(r) +} + +func (c *Conn) ObtainWriteIV() ([]byte, error) { + if len(c.writeIV) == c.IVSize() { + return c.writeIV, nil + } + + iv := make([]byte, c.IVSize()) + + if _, err := rand.Read(iv); err != nil { + return nil, err + } + + c.writeIV = iv + + return iv, nil +} + +func (c *Conn) ObtainReadIV() ([]byte, error) { + if len(c.readIV) == c.IVSize() { + return c.readIV, nil + } + + iv := make([]byte, c.IVSize()) + + if _, err := io.ReadFull(c.Conn, iv); err != nil { + return nil, err + } + + c.readIV = iv + + return iv, nil +} diff --git a/transport/snell/cipher.go b/transport/snell/cipher.go index 0f31aea5..24999e28 100644 --- a/transport/snell/cipher.go +++ b/transport/snell/cipher.go @@ -4,7 +4,8 @@ import ( "crypto/aes" "crypto/cipher" - "github.com/Dreamacro/go-shadowsocks2/shadowaead" + "github.com/Dreamacro/clash/transport/shadowsocks/shadowaead" + "golang.org/x/crypto/argon2" "golang.org/x/crypto/chacha20poly1305" ) diff --git a/transport/snell/pool.go b/transport/snell/pool.go index 62d21b4e..54b92344 100644 --- a/transport/snell/pool.go +++ b/transport/snell/pool.go @@ -6,8 +6,7 @@ import ( "time" "github.com/Dreamacro/clash/component/pool" - - "github.com/Dreamacro/go-shadowsocks2/shadowaead" + "github.com/Dreamacro/clash/transport/shadowsocks/shadowaead" ) type Pool struct { diff --git a/transport/snell/snell.go b/transport/snell/snell.go index 4cd5fba8..c6b7a569 100644 --- a/transport/snell/snell.go +++ b/transport/snell/snell.go @@ -9,9 +9,8 @@ import ( "sync" "github.com/Dreamacro/clash/common/pool" + "github.com/Dreamacro/clash/transport/shadowsocks/shadowaead" "github.com/Dreamacro/clash/transport/socks5" - - "github.com/Dreamacro/go-shadowsocks2/shadowaead" ) const ( diff --git a/transport/ssr/protocol/auth_chain_a.go b/transport/ssr/protocol/auth_chain_a.go index 906f8deb..6b12ab9b 100644 --- a/transport/ssr/protocol/auth_chain_a.go +++ b/transport/ssr/protocol/auth_chain_a.go @@ -13,9 +13,8 @@ import ( "github.com/Dreamacro/clash/common/pool" "github.com/Dreamacro/clash/log" + "github.com/Dreamacro/clash/transport/shadowsocks/core" "github.com/Dreamacro/clash/transport/ssr/tools" - - "github.com/Dreamacro/go-shadowsocks2/core" ) func init() { diff --git a/transport/ssr/protocol/base.go b/transport/ssr/protocol/base.go index 1ed348d0..4bf799b3 100644 --- a/transport/ssr/protocol/base.go +++ b/transport/ssr/protocol/base.go @@ -12,8 +12,7 @@ import ( "github.com/Dreamacro/clash/common/pool" "github.com/Dreamacro/clash/log" - - "github.com/Dreamacro/go-shadowsocks2/core" + "github.com/Dreamacro/clash/transport/shadowsocks/core" ) type Base struct { From 06d75da257d7960b906893e498eda53484559d61 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Sat, 11 Jun 2022 20:38:16 +0800 Subject: [PATCH 022/250] Chore: adjust Relay copy memory alloc logic --- common/net/relay.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/common/net/relay.go b/common/net/relay.go index e7157639..99a0c6ad 100644 --- a/common/net/relay.go +++ b/common/net/relay.go @@ -4,8 +4,6 @@ import ( "io" "net" "time" - - "github.com/Dreamacro/clash/common/pool" ) // Relay copies between left and right bidirectionally. @@ -13,18 +11,14 @@ func Relay(leftConn, rightConn net.Conn) { ch := make(chan error) go func() { - buf := pool.Get(pool.RelayBufferSize) // Wrapping to avoid using *net.TCPConn.(ReadFrom) // See also https://github.com/Dreamacro/clash/pull/1209 - _, err := io.CopyBuffer(WriteOnlyWriter{Writer: leftConn}, ReadOnlyReader{Reader: rightConn}, buf) - pool.Put(buf) + _, err := io.Copy(WriteOnlyWriter{Writer: leftConn}, ReadOnlyReader{Reader: rightConn}) leftConn.SetReadDeadline(time.Now()) ch <- err }() - buf := pool.Get(pool.RelayBufferSize) - io.CopyBuffer(WriteOnlyWriter{Writer: rightConn}, ReadOnlyReader{Reader: leftConn}, buf) - pool.Put(buf) + io.Copy(WriteOnlyWriter{Writer: rightConn}, ReadOnlyReader{Reader: leftConn}) rightConn.SetReadDeadline(time.Now()) <-ch } From 2fa1a5c4b98f24388a99a8e4279c79c82e52027f Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Sun, 12 Jun 2022 19:37:27 +0800 Subject: [PATCH 023/250] Chore: update tproxy udp packet read logic --- listener/tproxy/packet.go | 7 ++- listener/tproxy/{tproxy.go => tcp.go} | 0 listener/tproxy/udp.go | 11 ++-- listener/tproxy/udp_linux.go | 84 +++++++++++++-------------- listener/tproxy/udp_other.go | 7 ++- transport/socks5/socks5.go | 15 +++++ 6 files changed, 71 insertions(+), 53 deletions(-) rename listener/tproxy/{tproxy.go => tcp.go} (100%) diff --git a/listener/tproxy/packet.go b/listener/tproxy/packet.go index 8aa3e9bf..9299df9d 100644 --- a/listener/tproxy/packet.go +++ b/listener/tproxy/packet.go @@ -2,12 +2,13 @@ package tproxy import ( "net" + "net/netip" "github.com/Dreamacro/clash/common/pool" ) type packet struct { - lAddr *net.UDPAddr + lAddr netip.AddrPort buf []byte } @@ -17,7 +18,7 @@ func (c *packet) Data() []byte { // WriteBack opens a new socket binding `addr` to write UDP packet back func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) { - tc, err := dialUDP("udp", addr.(*net.UDPAddr), c.lAddr) + tc, err := dialUDP("udp", addr.(*net.UDPAddr).AddrPort(), c.lAddr) if err != nil { n = 0 return @@ -29,7 +30,7 @@ func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) { // LocalAddr returns the source IP/Port of UDP Packet func (c *packet) LocalAddr() net.Addr { - return c.lAddr + return &net.UDPAddr{IP: c.lAddr.Addr().AsSlice(), Port: int(c.lAddr.Port()), Zone: c.lAddr.Addr().Zone()} } func (c *packet) Drop() { diff --git a/listener/tproxy/tproxy.go b/listener/tproxy/tcp.go similarity index 100% rename from listener/tproxy/tproxy.go rename to listener/tproxy/tcp.go diff --git a/listener/tproxy/udp.go b/listener/tproxy/udp.go index c7e6d99e..60783563 100644 --- a/listener/tproxy/udp.go +++ b/listener/tproxy/udp.go @@ -2,6 +2,7 @@ package tproxy import ( "net" + "net/netip" "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/common/pool" @@ -58,7 +59,7 @@ func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (*UDPListener, error) oob := make([]byte, 1024) for { buf := pool.Get(pool.UDPBufferSize) - n, oobn, _, lAddr, err := c.ReadMsgUDP(buf, oob) + n, oobn, _, lAddr, err := c.ReadMsgUDPAddrPort(buf, oob) if err != nil { pool.Put(buf) if rl.closed { @@ -67,19 +68,19 @@ func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (*UDPListener, error) continue } - rAddr, err := getOrigDst(oob, oobn) + rAddr, err := getOrigDst(oob[:oobn]) if err != nil { continue } - handlePacketConn(l, in, buf[:n], lAddr, rAddr) + handlePacketConn(in, buf[:n], lAddr, rAddr) } }() return rl, nil } -func handlePacketConn(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buf []byte, lAddr *net.UDPAddr, rAddr *net.UDPAddr) { - target := socks5.ParseAddrToSocksAddr(rAddr) +func handlePacketConn(in chan<- *inbound.PacketAdapter, buf []byte, lAddr, rAddr netip.AddrPort) { + target := socks5.AddrFromStdAddrPort(rAddr) pkt := &packet{ lAddr: lAddr, buf: buf, diff --git a/listener/tproxy/udp_linux.go b/listener/tproxy/udp_linux.go index 8cda96fd..472a23d3 100644 --- a/listener/tproxy/udp_linux.go +++ b/listener/tproxy/udp_linux.go @@ -3,13 +3,14 @@ package tproxy import ( - "encoding/binary" - "errors" "fmt" "net" + "net/netip" "os" "strconv" "syscall" + + "golang.org/x/sys/unix" ) const ( @@ -19,7 +20,7 @@ const ( // dialUDP acts like net.DialUDP for transparent proxy. // It binds to a non-local address(`lAddr`). -func dialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) { +func dialUDP(network string, lAddr, rAddr netip.AddrPort) (uc *net.UDPConn, err error) { rSockAddr, err := udpAddrToSockAddr(rAddr) if err != nil { return nil, err @@ -35,23 +36,25 @@ func dialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPCo return nil, err } + defer func() { + if err != nil { + syscall.Close(fd) + } + }() + if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil { - syscall.Close(fd) return nil, err } if err = syscall.SetsockoptInt(fd, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil { - syscall.Close(fd) return nil, err } if err = syscall.Bind(fd, lSockAddr); err != nil { - syscall.Close(fd) return nil, err } if err = syscall.Connect(fd, rSockAddr); err != nil { - syscall.Close(fd) return nil, err } @@ -60,35 +63,26 @@ func dialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPCo c, err := net.FileConn(fdFile) if err != nil { - syscall.Close(fd) return nil, err } return c.(*net.UDPConn), nil } -func udpAddrToSockAddr(addr *net.UDPAddr) (syscall.Sockaddr, error) { - switch { - case addr.IP.To4() != nil: - ip := [4]byte{} - copy(ip[:], addr.IP.To4()) - - return &syscall.SockaddrInet4{Addr: ip, Port: addr.Port}, nil - - default: - ip := [16]byte{} - copy(ip[:], addr.IP.To16()) - - zoneID, err := strconv.ParseUint(addr.Zone, 10, 32) - if err != nil { - zoneID = 0 - } - - return &syscall.SockaddrInet6{Addr: ip, Port: addr.Port, ZoneId: uint32(zoneID)}, nil +func udpAddrToSockAddr(addr netip.AddrPort) (syscall.Sockaddr, error) { + if addr.Addr().Is4() { + return &syscall.SockaddrInet4{Addr: addr.Addr().As4(), Port: int(addr.Port())}, nil } + + zoneID, err := strconv.ParseUint(addr.Addr().Zone(), 10, 32) + if err != nil { + zoneID = 0 + } + + return &syscall.SockaddrInet6{Addr: addr.Addr().As16(), Port: int(addr.Port()), ZoneId: uint32(zoneID)}, nil } -func udpAddrFamily(net string, lAddr, rAddr *net.UDPAddr) int { +func udpAddrFamily(net string, lAddr, rAddr netip.AddrPort) int { switch net[len(net)-1] { case '4': return syscall.AF_INET @@ -96,29 +90,35 @@ func udpAddrFamily(net string, lAddr, rAddr *net.UDPAddr) int { return syscall.AF_INET6 } - if (lAddr == nil || lAddr.IP.To4() != nil) && (rAddr == nil || lAddr.IP.To4() != nil) { + if lAddr.Addr().Is4() && rAddr.Addr().Is4() { return syscall.AF_INET } return syscall.AF_INET6 } -func getOrigDst(oob []byte, oobn int) (*net.UDPAddr, error) { - msgs, err := syscall.ParseSocketControlMessage(oob[:oobn]) +func getOrigDst(oob []byte) (netip.AddrPort, error) { + // oob contains socket control messages which we need to parse. + scms, err := unix.ParseSocketControlMessage(oob) if err != nil { - return nil, err + return netip.AddrPort{}, fmt.Errorf("parse control message: %w", err) } - for _, msg := range msgs { - if msg.Header.Level == syscall.SOL_IP && msg.Header.Type == syscall.IP_RECVORIGDSTADDR { - ip := net.IP(msg.Data[4:8]) - port := binary.BigEndian.Uint16(msg.Data[2:4]) - return &net.UDPAddr{IP: ip, Port: int(port)}, nil - } else if msg.Header.Level == syscall.SOL_IPV6 && msg.Header.Type == IPV6_RECVORIGDSTADDR { - ip := net.IP(msg.Data[8:24]) - port := binary.BigEndian.Uint16(msg.Data[2:4]) - return &net.UDPAddr{IP: ip, Port: int(port)}, nil - } + // retrieve the destination address from the SCM. + sa, err := unix.ParseOrigDstAddr(&scms[0]) + if err != nil { + return netip.AddrPort{}, fmt.Errorf("retrieve destination: %w", err) } - return nil, errors.New("cannot find origDst") + // encode the destination address into a cmsg. + var rAddr netip.AddrPort + switch v := sa.(type) { + case *unix.SockaddrInet4: + rAddr = netip.AddrPortFrom(netip.AddrFrom4(v.Addr), uint16(v.Port)) + case *unix.SockaddrInet6: + rAddr = netip.AddrPortFrom(netip.AddrFrom16(v.Addr), uint16(v.Port)) + default: + return netip.AddrPort{}, fmt.Errorf("unsupported address type: %T", v) + } + + return rAddr, nil } diff --git a/listener/tproxy/udp_other.go b/listener/tproxy/udp_other.go index db4a1409..b35b07dd 100644 --- a/listener/tproxy/udp_other.go +++ b/listener/tproxy/udp_other.go @@ -5,12 +5,13 @@ package tproxy import ( "errors" "net" + "net/netip" ) -func getOrigDst(oob []byte, oobn int) (*net.UDPAddr, error) { - return nil, errors.New("UDP redir not supported on current platform") +func getOrigDst(oob []byte) (netip.AddrPort, error) { + return netip.AddrPort{}, errors.New("UDP redir not supported on current platform") } -func dialUDP(network string, lAddr *net.UDPAddr, rAddr *net.UDPAddr) (*net.UDPConn, error) { +func dialUDP(network string, lAddr, rAddr netip.AddrPort) (*net.UDPConn, error) { return nil, errors.New("UDP redir not supported on current platform") } diff --git a/transport/socks5/socks5.go b/transport/socks5/socks5.go index 2950819a..b051827b 100644 --- a/transport/socks5/socks5.go +++ b/transport/socks5/socks5.go @@ -398,6 +398,21 @@ func ParseAddrToSocksAddr(addr net.Addr) Addr { return parsed } +func AddrFromStdAddrPort(addrPort netip.AddrPort) Addr { + addr := addrPort.Addr() + if addr.Is4() { + ip4 := addr.As4() + return []byte{AtypIPv4, ip4[0], ip4[1], ip4[2], ip4[3], byte(addrPort.Port() >> 8), byte(addrPort.Port())} + } + + buf := make([]byte, 1+net.IPv6len+2) + buf[0] = AtypIPv6 + copy(buf[1:], addr.AsSlice()) + buf[1+net.IPv6len] = byte(addrPort.Port() >> 8) + buf[1+net.IPv6len+1] = byte(addrPort.Port()) + return buf +} + // DecodeUDPPacket split `packet` to addr payload, and this function is mutable with `packet` func DecodeUDPPacket(packet []byte) (addr Addr, payload []byte, err error) { if len(packet) < 5 { From e0c5a85314a34637dcd2ee57fbba480d3f24b949 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Sun, 12 Jun 2022 21:22:02 +0800 Subject: [PATCH 024/250] Fix: missing import --- transport/socks5/socks5.go | 1 + 1 file changed, 1 insertion(+) diff --git a/transport/socks5/socks5.go b/transport/socks5/socks5.go index b051827b..7d4f11ae 100644 --- a/transport/socks5/socks5.go +++ b/transport/socks5/socks5.go @@ -6,6 +6,7 @@ import ( "errors" "io" "net" + "net/netip" "strconv" "github.com/Dreamacro/clash/component/auth" From fbb27b84d1b0d1fac45458b4dc4dfc30de961e56 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Tue, 14 Jun 2022 11:26:04 +0800 Subject: [PATCH 025/250] Chore: add redir-host deprecated warnning --- hub/executor/executor.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 9a9cc81b..dc1f8601 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -130,6 +130,11 @@ func updateDNS(c *config.DNS) { Policy: c.NameServerPolicy, } + // deprecated warnning + if cfg.EnhancedMode == C.DNSMapping { + log.Warnln("[DNS] %s is deprecated, please use %s instead", cfg.EnhancedMode.String(), C.DNSFakeIP.String()) + } + r := dns.NewResolver(cfg) m := dns.NewEnhancer(cfg) From c231fd14666d6ea05d6a75eaba6db69f9eee5ae9 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Sun, 19 Jun 2022 13:01:43 +0800 Subject: [PATCH 026/250] Chore: update dependencies --- go.mod | 14 +++++++------- go.sum | 27 ++++++++++++++------------- test/go.mod | 14 +++++++------- test/go.sum | 27 ++++++++++++++------------- 4 files changed, 42 insertions(+), 40 deletions(-) diff --git a/go.mod b/go.mod index 6dfc58ac..2ee56b99 100644 --- a/go.mod +++ b/go.mod @@ -9,18 +9,18 @@ require ( github.com/gofrs/uuid v4.2.0+incompatible github.com/gorilla/websocket v1.5.0 github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f - github.com/miekg/dns v1.1.48 + github.com/miekg/dns v1.1.49 github.com/oschwald/geoip2-golang v1.7.0 github.com/sirupsen/logrus v1.8.1 - github.com/stretchr/testify v1.7.1 + github.com/stretchr/testify v1.7.2 go.etcd.io/bbolt v1.3.6 go.uber.org/atomic v1.9.0 go.uber.org/automaxprocs v1.5.1 - golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 - golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c - golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 - gopkg.in/yaml.v3 v3.0.0 + golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e + golang.org/x/net v0.0.0-20220617184016-355a448f1bc9 + golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f + golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c + gopkg.in/yaml.v3 v3.0.1 ) require ( diff --git a/go.sum b/go.sum index 5ffa36ce..d8b4341f 100644 --- a/go.sum +++ b/go.sum @@ -37,8 +37,8 @@ github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcK github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= -github.com/miekg/dns v1.1.48 h1:Ucfr7IIVyMBz4lRE8qmGUuZ4Wt3/ZGu9hmcMT3Uu4tQ= -github.com/miekg/dns v1.1.48/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/miekg/dns v1.1.49 h1:qe0mQU3Z/XpFeE+AEBo2rqaS1IPBJ3anmqZ4XiZJVG8= +github.com/miekg/dns v1.1.49/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/oschwald/geoip2-golang v1.7.0 h1:JW1r5AKi+vv2ujSxjKthySK3jo8w8oKWPyXsw+Qs/S8= github.com/oschwald/geoip2-golang v1.7.0/go.mod h1:mdI/C7iK7NVMcIDDtf4bCKMJ7r0o7UwGeCo9eiitCMQ= github.com/oschwald/maxminddb-golang v1.9.0 h1:tIk4nv6VT9OiPyrnDAfJS1s1xKDQMZOsGojab6EjC1Y= @@ -54,8 +54,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA= github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= @@ -68,8 +68,8 @@ go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66 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.0.0-20220507011949-2cf3adece122 h1:NvGWuYG8dkDHFSKksI1P9faiVJ9rayE6l0+ouWVIDs8= -golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -84,11 +84,12 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9 h1:Yqz/iviulwKwAREEeUd3nbBFn0XuyJqkoft2IlrvOhc= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -109,8 +110,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -130,5 +131,5 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/test/go.mod b/test/go.mod index 3af6733c..9606d992 100644 --- a/test/go.mod +++ b/test/go.mod @@ -4,11 +4,11 @@ go 1.18 require ( github.com/Dreamacro/clash v1.10.6 - github.com/docker/docker v20.10.16+incompatible + github.com/docker/docker v20.10.17+incompatible github.com/docker/go-connections v0.4.0 github.com/miekg/dns v1.1.49 - github.com/stretchr/testify v1.7.1 - golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 + github.com/stretchr/testify v1.7.2 + golang.org/x/net v0.0.0-20220617184016-355a448f1bc9 ) replace github.com/Dreamacro/clash => ../ @@ -35,15 +35,15 @@ require ( github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect go.etcd.io/bbolt v1.3.6 // indirect go.uber.org/atomic v1.9.0 // indirect - golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 // indirect + golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect golang.org/x/mod v0.4.2 // indirect - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect - golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect + golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect + golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect - gopkg.in/yaml.v3 v3.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.1.0 // indirect ) diff --git a/test/go.sum b/test/go.sum index f68bd669..90e97dc7 100644 --- a/test/go.sum +++ b/test/go.sum @@ -8,8 +8,8 @@ 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/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.16+incompatible h1:2Db6ZR/+FUR3hqPMwnogOPHFn405crbpxvWzKovETOQ= -github.com/docker/docker v20.10.16+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE= +github.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= @@ -81,8 +81,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA= github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -95,8 +95,8 @@ go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 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.0.0-20220507011949-2cf3adece122 h1:NvGWuYG8dkDHFSKksI1P9faiVJ9rayE6l0+ouWVIDs8= -golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= +golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 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.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= @@ -115,13 +115,14 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y= -golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9 h1:Yqz/iviulwKwAREEeUd3nbBFn0XuyJqkoft2IlrvOhc= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 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.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -145,8 +146,8 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -173,8 +174,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.1.0 h1:rVV8Tcg/8jHUkPUorwjaMTtemIMVXfIPKiOqnhEhakk= gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ= From fc5c9b931b429908c4f68af4b4cb93e55339c5f8 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Wed, 29 Jun 2022 23:36:24 +0800 Subject: [PATCH 027/250] Fix: try to unmap lAddr on tproxy udp listener --- listener/tproxy/udp.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/listener/tproxy/udp.go b/listener/tproxy/udp.go index 60783563..0e7c059f 100644 --- a/listener/tproxy/udp.go +++ b/listener/tproxy/udp.go @@ -72,6 +72,11 @@ func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (*UDPListener, error) if err != nil { continue } + + if rAddr.Addr().Is4() { + // try to unmap 4in6 address + lAddr = netip.AddrPortFrom(lAddr.Addr().Unmap(), lAddr.Port()) + } handlePacketConn(in, buf[:n], lAddr, rAddr) } }() From 91c22b16bfe1b40128ab9c3fa8686cd53b59b99b Mon Sep 17 00:00:00 2001 From: Kaming Chan Date: Thu, 30 Jun 2022 17:08:53 +0800 Subject: [PATCH 028/250] Fix: proxy provider filter validation (#2198) --- adapter/provider/provider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index e3bc4947..442b4b44 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -129,7 +129,7 @@ func NewProxySetProvider(name string, interval time.Duration, filter string, veh proxies := []C.Proxy{} for idx, mapping := range schema.Proxies { - if name, ok := mapping["name"]; ok && len(filter) > 0 && !filterReg.MatchString(name.(string)) { + if name, ok := mapping["name"].(string); ok && len(filter) > 0 && !filterReg.MatchString(name) { continue } proxy, err := adapter.ParseProxy(mapping) From 27e1d6cdaeef575266f5aa12f33e2e9d0d4938ff Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Thu, 30 Jun 2022 17:12:06 +0800 Subject: [PATCH 029/250] Chore: cleanup code --- adapter/outboundgroup/loadbalance.go | 6 ++---- adapter/outboundgroup/urltest.go | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/adapter/outboundgroup/loadbalance.go b/adapter/outboundgroup/loadbalance.go index 380b0d06..f396ff04 100644 --- a/adapter/outboundgroup/loadbalance.go +++ b/adapter/outboundgroup/loadbalance.go @@ -30,10 +30,8 @@ type LoadBalance struct { var errStrategy = errors.New("unsupported strategy") func parseStrategy(config map[string]any) string { - if elm, ok := config["strategy"]; ok { - if strategy, ok := elm.(string); ok { - return strategy - } + if strategy, ok := config["strategy"].(string); ok { + return strategy } return "consistent-hashing" } diff --git a/adapter/outboundgroup/urltest.go b/adapter/outboundgroup/urltest.go index 61597498..fd6d6818 100644 --- a/adapter/outboundgroup/urltest.go +++ b/adapter/outboundgroup/urltest.go @@ -125,10 +125,8 @@ func parseURLTestOption(config map[string]any) []urlTestOption { opts := []urlTestOption{} // tolerance - if elm, ok := config["tolerance"]; ok { - if tolerance, ok := elm.(int); ok { - opts = append(opts, urlTestWithTolerance(uint16(tolerance))) - } + if tolerance, ok := config["tolerance"].(int); ok { + opts = append(opts, urlTestWithTolerance(uint16(tolerance))) } return opts From 77a1e3a6532e3892dc061a98e85b302d9bc6cc56 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Thu, 30 Jun 2022 17:27:38 +0800 Subject: [PATCH 030/250] Chore: cleanup bind mark code --- component/dialer/mark_linux.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/component/dialer/mark_linux.go b/component/dialer/mark_linux.go index e54873bc..759097df 100644 --- a/component/dialer/mark_linux.go +++ b/component/dialer/mark_linux.go @@ -31,13 +31,13 @@ func bindMarkToControl(mark int, chain controlFn) controlFn { } } - return c.Control(func(fd uintptr) { - switch network { - case "tcp4", "udp4": - syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, mark) - case "tcp6", "udp6": - syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, mark) - } + var innerErr error + err = c.Control(func(fd uintptr) { + innerErr = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, mark) }) + if innerErr != nil { + err = innerErr + } + return } } From 557297ac9a87394d9133f28e0f9527fb607197e3 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Mon, 4 Jul 2022 21:36:33 +0800 Subject: [PATCH 031/250] Chore: load balance hash need to have fallback strategy --- adapter/outboundgroup/loadbalance.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/adapter/outboundgroup/loadbalance.go b/adapter/outboundgroup/loadbalance.go index f396ff04..95fee89d 100644 --- a/adapter/outboundgroup/loadbalance.go +++ b/adapter/outboundgroup/loadbalance.go @@ -127,6 +127,13 @@ func strategyConsistentHashing() strategyFn { } } + // when availability is poor, traverse the entire list to get the available nodes + for _, proxy := range proxies { + if proxy.Alive() { + return proxy + } + } + return proxies[0] } } From aaf08dadffbb4e9e6404e6d61c1b9ab94479d6fa Mon Sep 17 00:00:00 2001 From: Dreamacro Date: Tue, 5 Jul 2022 20:26:43 +0800 Subject: [PATCH 032/250] Change: remove AddrType on Metadata (#2199) --- adapter/adapter.go | 7 +++---- adapter/inbound/util.go | 22 ++++++---------------- adapter/outbound/util.go | 5 +++-- adapter/outbound/vmess.go | 9 +++++---- adapter/outboundgroup/util.go | 21 +++++++++------------ constant/metadata.go | 23 +++++++++++++---------- rule/domain.go | 3 --- rule/domain_keyword.go | 6 +----- rule/domain_suffix.go | 3 --- tunnel/tunnel.go | 6 ------ 10 files changed, 40 insertions(+), 65 deletions(-) diff --git a/adapter/adapter.go b/adapter/adapter.go index 23dc304a..c41cff4d 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -184,10 +184,9 @@ func urlToMetadata(rawURL string) (addr C.Metadata, err error) { } addr = C.Metadata{ - AddrType: C.AtypDomainName, - Host: u.Hostname(), - DstIP: nil, - DstPort: port, + Host: u.Hostname(), + DstIP: nil, + DstPort: port, } return } diff --git a/adapter/inbound/util.go b/adapter/inbound/util.go index 07577ec0..4f139270 100644 --- a/adapter/inbound/util.go +++ b/adapter/inbound/util.go @@ -11,9 +11,7 @@ import ( ) func parseSocksAddr(target socks5.Addr) *C.Metadata { - metadata := &C.Metadata{ - AddrType: int(target[0]), - } + metadata := &C.Metadata{} switch target[0] { case socks5.AtypDomainName: @@ -44,21 +42,13 @@ func parseHTTPAddr(request *http.Request) *C.Metadata { host = strings.TrimRight(host, ".") metadata := &C.Metadata{ - NetWork: C.TCP, - AddrType: C.AtypDomainName, - Host: host, - DstIP: nil, - DstPort: port, + NetWork: C.TCP, + Host: host, + DstIP: nil, + DstPort: port, } - ip := net.ParseIP(host) - if ip != nil { - switch { - case ip.To4() == nil: - metadata.AddrType = C.AtypIPv6 - default: - metadata.AddrType = C.AtypIPv4 - } + if ip := net.ParseIP(host); ip != nil { metadata.DstIP = ip } diff --git a/adapter/outbound/util.go b/adapter/outbound/util.go index b376522f..a964a141 100644 --- a/adapter/outbound/util.go +++ b/adapter/outbound/util.go @@ -20,10 +20,11 @@ func tcpKeepAlive(c net.Conn) { func serializesSocksAddr(metadata *C.Metadata) []byte { var buf [][]byte - aType := uint8(metadata.AddrType) + addrType := metadata.AddrType() + aType := uint8(addrType) p, _ := strconv.ParseUint(metadata.DstPort, 10, 16) port := []byte{uint8(p >> 8), uint8(p & 0xff)} - switch metadata.AddrType { + switch addrType { case socks5.AtypDomainName: len := uint8(len(metadata.Host)) host := []byte(metadata.Host) diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index ea3681a3..31d0a2cf 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -14,6 +14,7 @@ import ( "github.com/Dreamacro/clash/component/resolver" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/transport/gun" + "github.com/Dreamacro/clash/transport/socks5" "github.com/Dreamacro/clash/transport/vmess" "golang.org/x/net/http2" @@ -327,16 +328,16 @@ func NewVmess(option VmessOption) (*Vmess, error) { func parseVmessAddr(metadata *C.Metadata) *vmess.DstAddr { var addrType byte var addr []byte - switch metadata.AddrType { - case C.AtypIPv4: + switch metadata.AddrType() { + case socks5.AtypIPv4: addrType = byte(vmess.AtypIPv4) addr = make([]byte, net.IPv4len) copy(addr[:], metadata.DstIP.To4()) - case C.AtypIPv6: + case socks5.AtypIPv6: addrType = byte(vmess.AtypIPv6) addr = make([]byte, net.IPv6len) copy(addr[:], metadata.DstIP.To16()) - case C.AtypDomainName: + case socks5.AtypDomainName: addrType = byte(vmess.AtypDomainName) addr = make([]byte, len(metadata.Host)+1) addr[0] = byte(len(metadata.Host)) diff --git a/adapter/outboundgroup/util.go b/adapter/outboundgroup/util.go index 70d71078..1ec0254a 100644 --- a/adapter/outboundgroup/util.go +++ b/adapter/outboundgroup/util.go @@ -18,27 +18,24 @@ func addrToMetadata(rawAddress string) (addr *C.Metadata, err error) { ip := net.ParseIP(host) if ip == nil { addr = &C.Metadata{ - AddrType: C.AtypDomainName, - Host: host, - DstIP: nil, - DstPort: port, + Host: host, + DstIP: nil, + DstPort: port, } return } else if ip4 := ip.To4(); ip4 != nil { addr = &C.Metadata{ - AddrType: C.AtypIPv4, - Host: "", - DstIP: ip4, - DstPort: port, + Host: "", + DstIP: ip4, + DstPort: port, } return } addr = &C.Metadata{ - AddrType: C.AtypIPv6, - Host: "", - DstIP: ip, - DstPort: port, + Host: "", + DstIP: ip, + DstPort: port, } return } diff --git a/constant/metadata.go b/constant/metadata.go index 6789c913..b829b7b0 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -4,14 +4,12 @@ import ( "encoding/json" "net" "strconv" + + "github.com/Dreamacro/clash/transport/socks5" ) // Socks addr type const ( - AtypIPv4 = 1 - AtypDomainName = 3 - AtypIPv6 = 4 - TCP NetWork = iota UDP @@ -69,7 +67,6 @@ type Metadata struct { DstIP net.IP `json:"destinationIP"` SrcPort string `json:"sourcePort"` DstPort string `json:"destinationPort"` - AddrType int `json:"-"` Host string `json:"host"` DNSMode DNSMode `json:"dnsMode"` ProcessPath string `json:"processPath"` @@ -83,6 +80,17 @@ func (m *Metadata) SourceAddress() string { return net.JoinHostPort(m.SrcIP.String(), m.SrcPort) } +func (m *Metadata) AddrType() int { + switch true { + case m.Host != "" || m.DstIP == nil: + return socks5.AtypDomainName + case m.DstIP.To4() != nil: + return socks5.AtypIPv4 + default: + return socks5.AtypIPv6 + } +} + func (m *Metadata) Resolved() bool { return m.DstIP != nil } @@ -93,11 +101,6 @@ func (m *Metadata) Pure() *Metadata { if m.DNSMode == DNSMapping && m.DstIP != nil { copy := *m copy.Host = "" - if copy.DstIP.To4() != nil { - copy.AddrType = AtypIPv4 - } else { - copy.AddrType = AtypIPv6 - } return © } diff --git a/rule/domain.go b/rule/domain.go index 2b2a076b..94978e6d 100644 --- a/rule/domain.go +++ b/rule/domain.go @@ -16,9 +16,6 @@ func (d *Domain) RuleType() C.RuleType { } func (d *Domain) Match(metadata *C.Metadata) bool { - if metadata.AddrType != C.AtypDomainName { - return false - } return metadata.Host == d.domain } diff --git a/rule/domain_keyword.go b/rule/domain_keyword.go index 046eafe4..9e661120 100644 --- a/rule/domain_keyword.go +++ b/rule/domain_keyword.go @@ -16,11 +16,7 @@ func (dk *DomainKeyword) RuleType() C.RuleType { } func (dk *DomainKeyword) Match(metadata *C.Metadata) bool { - if metadata.AddrType != C.AtypDomainName { - return false - } - domain := metadata.Host - return strings.Contains(domain, dk.keyword) + return strings.Contains(metadata.Host, dk.keyword) } func (dk *DomainKeyword) Adapter() string { diff --git a/rule/domain_suffix.go b/rule/domain_suffix.go index 09219338..70b8cfe5 100644 --- a/rule/domain_suffix.go +++ b/rule/domain_suffix.go @@ -16,9 +16,6 @@ func (ds *DomainSuffix) RuleType() C.RuleType { } func (ds *DomainSuffix) Match(metadata *C.Metadata) bool { - if metadata.AddrType != C.AtypDomainName { - return false - } domain := metadata.Host return strings.HasSuffix(domain, "."+ds.suffix) || domain == ds.suffix } diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index f9e13016..6a39ce70 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -122,11 +122,6 @@ func preHandleMetadata(metadata *C.Metadata) error { if ip := net.ParseIP(metadata.Host); ip != nil { metadata.DstIP = ip metadata.Host = "" - if ip.To4() != nil { - metadata.AddrType = C.AtypIPv4 - } else { - metadata.AddrType = C.AtypIPv6 - } } // preprocess enhanced-mode metadata @@ -134,7 +129,6 @@ func preHandleMetadata(metadata *C.Metadata) error { host, exist := resolver.FindHostByIP(metadata.DstIP) if exist { metadata.Host = host - metadata.AddrType = C.AtypDomainName metadata.DNSMode = C.DNSMapping if resolver.FakeIPEnabled() { metadata.DstIP = nil From 5497adaba1ff81be5225ebd0849964bba5b23987 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Tue, 5 Jul 2022 21:09:29 +0800 Subject: [PATCH 033/250] Fix: fakeip udp should not replace with another ip --- tunnel/connection.go | 24 ++++++++++-------------- tunnel/tunnel.go | 19 ++++++++++++++++--- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/tunnel/connection.go b/tunnel/connection.go index 0384e805..cf024c75 100644 --- a/tunnel/connection.go +++ b/tunnel/connection.go @@ -3,26 +3,17 @@ package tunnel import ( "errors" "net" + "net/netip" "time" N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/pool" - "github.com/Dreamacro/clash/component/resolver" C "github.com/Dreamacro/clash/constant" ) func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata) error { defer packet.Drop() - // local resolve UDP dns - if !metadata.Resolved() { - ip, err := resolver.ResolveIP(metadata.Host) - if err != nil { - return err - } - metadata.DstIP = ip - } - addr := metadata.UDPAddr() if addr == nil { return errors.New("udp addr invalid") @@ -37,7 +28,7 @@ func handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, metadata *C.Metadata return nil } -func handleUDPToLocal(packet C.UDPPacket, pc net.PacketConn, key string, fAddr net.Addr) { +func handleUDPToLocal(packet C.UDPPacket, pc net.PacketConn, key string, oAddr, fAddr netip.Addr) { buf := pool.Get(pool.UDPBufferSize) defer pool.Put(buf) defer natTable.Delete(key) @@ -50,11 +41,16 @@ func handleUDPToLocal(packet C.UDPPacket, pc net.PacketConn, key string, fAddr n return } - if fAddr != nil { - from = fAddr + fromUDPAddr := from.(*net.UDPAddr) + if fAddr.IsValid() { + fromAddr, _ := netip.AddrFromSlice(fromUDPAddr.IP) + fromAddr.Unmap() + if oAddr == fromAddr { + fromUDPAddr.IP = fAddr.AsSlice() + } } - _, err = packet.WriteBack(buf[:n], from) + _, err = packet.WriteBack(buf[:n], fromUDPAddr) if err != nil { return } diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 6a39ce70..4a4685b2 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "net" + "net/netip" "runtime" "strconv" "sync" @@ -166,9 +167,10 @@ func handleUDPConn(packet *inbound.PacketAdapter) { } // make a fAddr if request ip is fakeip - var fAddr net.Addr + var fAddr netip.Addr if resolver.IsExistFakeIP(metadata.DstIP) { - fAddr = metadata.UDPAddr() + fAddr, _ = netip.AddrFromSlice(metadata.DstIP) + fAddr = fAddr.Unmap() } if err := preHandleMetadata(metadata); err != nil { @@ -176,6 +178,15 @@ func handleUDPConn(packet *inbound.PacketAdapter) { return } + // local resolve UDP dns + if !metadata.Resolved() { + ip, err := resolver.ResolveIP(metadata.Host) + if err != nil { + return + } + metadata.DstIP = ip + } + key := packet.LocalAddr().String() handle := func() bool { @@ -240,7 +251,9 @@ func handleUDPConn(packet *inbound.PacketAdapter) { log.Infoln("[UDP] %s --> %s doesn't match any rule using DIRECT", metadata.SourceAddress(), metadata.RemoteAddress()) } - go handleUDPToLocal(packet.UDPPacket, pc, key, fAddr) + oAddr, _ := netip.AddrFromSlice(metadata.DstIP) + oAddr = oAddr.Unmap() + go handleUDPToLocal(packet.UDPPacket, pc, key, oAddr, fAddr) natTable.Set(key, pc) handle() From 65a8e8f59ccc4c51c8a4581fa985f23550c1fa55 Mon Sep 17 00:00:00 2001 From: Kaming Chan Date: Wed, 6 Jul 2022 13:44:04 +0800 Subject: [PATCH 034/250] Fix: process rule type (#2206) --- rule/process.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rule/process.go b/rule/process.go index 52f0cec2..1c9f43bc 100644 --- a/rule/process.go +++ b/rule/process.go @@ -14,7 +14,11 @@ type Process struct { } func (ps *Process) RuleType() C.RuleType { - return C.Process + if ps.nameOnly { + return C.Process + } + + return C.ProcessPath } func (ps *Process) Match(metadata *C.Metadata) bool { From 8c7c8f43741923d6a7650dc723b4b51b0f540777 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Thu, 7 Jul 2022 22:15:50 +0800 Subject: [PATCH 035/250] Chore: update dependencies --- go.mod | 10 +++++----- go.sum | 22 ++++++++++++---------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index 2ee56b99..42d4d055 100644 --- a/go.mod +++ b/go.mod @@ -9,17 +9,17 @@ require ( github.com/gofrs/uuid v4.2.0+incompatible github.com/gorilla/websocket v1.5.0 github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f - github.com/miekg/dns v1.1.49 + github.com/miekg/dns v1.1.50 github.com/oschwald/geoip2-golang v1.7.0 github.com/sirupsen/logrus v1.8.1 - github.com/stretchr/testify v1.7.2 + github.com/stretchr/testify v1.8.0 go.etcd.io/bbolt v1.3.6 go.uber.org/atomic v1.9.0 go.uber.org/automaxprocs v1.5.1 - golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e - golang.org/x/net v0.0.0-20220617184016-355a448f1bc9 + golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d + golang.org/x/net v0.0.0-20220706163947-c90051bbdb60 golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f - golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c + golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index d8b4341f..9d5eabca 100644 --- a/go.sum +++ b/go.sum @@ -37,8 +37,8 @@ github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcK github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= -github.com/miekg/dns v1.1.49 h1:qe0mQU3Z/XpFeE+AEBo2rqaS1IPBJ3anmqZ4XiZJVG8= -github.com/miekg/dns v1.1.49/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= +github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/oschwald/geoip2-golang v1.7.0 h1:JW1r5AKi+vv2ujSxjKthySK3jo8w8oKWPyXsw+Qs/S8= github.com/oschwald/geoip2-golang v1.7.0/go.mod h1:mdI/C7iK7NVMcIDDtf4bCKMJ7r0o7UwGeCo9eiitCMQ= github.com/oschwald/maxminddb-golang v1.9.0 h1:tIk4nv6VT9OiPyrnDAfJS1s1xKDQMZOsGojab6EjC1Y= @@ -51,11 +51,13 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA= github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= @@ -68,8 +70,8 @@ go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66 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.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -84,8 +86,8 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220617184016-355a448f1bc9 h1:Yqz/iviulwKwAREEeUd3nbBFn0XuyJqkoft2IlrvOhc= -golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220706163947-c90051bbdb60 h1:8NSylCMxLW4JvserAndSgFL7aPli6A68yf0bYFTcWCM= +golang.org/x/net v0.0.0-20220706163947-c90051bbdb60/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8= @@ -110,8 +112,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e h1:CsOuNlbOuf0mzxJIefr6Q4uAUetRUwZE4qt7VfzP+xo= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= From f0952b55d01c6d57b49758d63eef33b3538b54cc Mon Sep 17 00:00:00 2001 From: LJea Date: Sun, 10 Jul 2022 15:56:34 +0900 Subject: [PATCH 036/250] Fix: query string parse on `ws-opts` (#2213) --- transport/vmess/websocket.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/transport/vmess/websocket.go b/transport/vmess/websocket.go index f769dcce..b7b369fd 100644 --- a/transport/vmess/websocket.go +++ b/transport/vmess/websocket.go @@ -256,10 +256,16 @@ func streamWebsocketConn(conn net.Conn, c *WebsocketConfig, earlyData *bytes.Buf dialer.TLSClientConfig = c.TLSConfig } + u, err := url.Parse(c.Path) + if err != nil { + return nil, fmt.Errorf("parse url %s error: %w", c.Path, err) + } + uri := url.URL{ - Scheme: scheme, - Host: net.JoinHostPort(c.Host, c.Port), - Path: c.Path, + Scheme: scheme, + Host: net.JoinHostPort(c.Host, c.Port), + Path: u.Path, + RawQuery: u.RawQuery, } headers := http.Header{} From 3b90e18047f25b9db3469bac05fc3dd89dcbe5fa Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Fri, 15 Jul 2022 16:07:18 +0800 Subject: [PATCH 037/250] Chore: update test dependencies --- test/clash_test.go | 36 +++++++++++++++--------------------- test/go.mod | 12 ++++++------ test/go.sum | 22 ++++++++++++---------- 3 files changed, 33 insertions(+), 37 deletions(-) diff --git a/test/clash_test.go b/test/clash_test.go index 7c9ca051..3bf7a592 100644 --- a/test/clash_test.go +++ b/test/clash_test.go @@ -542,18 +542,16 @@ func testPacketConnTimeout(t *testing.T, pc net.PacketConn) error { func testSuit(t *testing.T, proxy C.ProxyAdapter) { conn, err := proxy.DialContext(context.Background(), &C.Metadata{ - Host: localIP.String(), - DstPort: "10001", - AddrType: socks5.AtypDomainName, + Host: localIP.String(), + DstPort: "10001", }) require.NoError(t, err) defer conn.Close() assert.NoError(t, testPingPongWithConn(t, conn)) conn, err = proxy.DialContext(context.Background(), &C.Metadata{ - Host: localIP.String(), - DstPort: "10001", - AddrType: socks5.AtypDomainName, + Host: localIP.String(), + DstPort: "10001", }) require.NoError(t, err) defer conn.Close() @@ -564,10 +562,9 @@ func testSuit(t *testing.T, proxy C.ProxyAdapter) { } pc, err := proxy.ListenPacketContext(context.Background(), &C.Metadata{ - NetWork: C.UDP, - DstIP: localIP, - DstPort: "10001", - AddrType: socks5.AtypIPv4, + NetWork: C.UDP, + DstIP: localIP, + DstPort: "10001", }) require.NoError(t, err) defer pc.Close() @@ -575,10 +572,9 @@ func testSuit(t *testing.T, proxy C.ProxyAdapter) { assert.NoError(t, testPingPongWithPacketConn(t, pc)) pc, err = proxy.ListenPacketContext(context.Background(), &C.Metadata{ - NetWork: C.UDP, - DstIP: localIP, - DstPort: "10001", - AddrType: socks5.AtypIPv4, + NetWork: C.UDP, + DstIP: localIP, + DstPort: "10001", }) require.NoError(t, err) defer pc.Close() @@ -586,10 +582,9 @@ func testSuit(t *testing.T, proxy C.ProxyAdapter) { assert.NoError(t, testLargeDataWithPacketConn(t, pc)) pc, err = proxy.ListenPacketContext(context.Background(), &C.Metadata{ - NetWork: C.UDP, - DstIP: localIP, - DstPort: "10001", - AddrType: socks5.AtypIPv4, + NetWork: C.UDP, + DstIP: localIP, + DstPort: "10001", }) require.NoError(t, err) defer pc.Close() @@ -625,9 +620,8 @@ func benchmarkProxy(b *testing.B, proxy C.ProxyAdapter) { }() conn, err := proxy.DialContext(context.Background(), &C.Metadata{ - Host: localIP.String(), - DstPort: "10001", - AddrType: socks5.AtypDomainName, + Host: localIP.String(), + DstPort: "10001", }) require.NoError(b, err) diff --git a/test/go.mod b/test/go.mod index 9606d992..f61ed9d4 100644 --- a/test/go.mod +++ b/test/go.mod @@ -3,12 +3,12 @@ module clash-test go 1.18 require ( - github.com/Dreamacro/clash v1.10.6 + github.com/Dreamacro/clash v1.11.4 github.com/docker/docker v20.10.17+incompatible github.com/docker/go-connections v0.4.0 - github.com/miekg/dns v1.1.49 - github.com/stretchr/testify v1.7.2 - golang.org/x/net v0.0.0-20220617184016-355a448f1bc9 + github.com/miekg/dns v1.1.50 + github.com/stretchr/testify v1.8.0 + golang.org/x/net v0.0.0-20220708220712-1185a9018129 ) replace github.com/Dreamacro/clash => ../ @@ -35,10 +35,10 @@ require ( github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect go.etcd.io/bbolt v1.3.6 // indirect go.uber.org/atomic v1.9.0 // indirect - golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect + golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect golang.org/x/mod v0.4.2 // indirect golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect - golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c // indirect + golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect diff --git a/test/go.sum b/test/go.sum index 90e97dc7..be43b1f0 100644 --- a/test/go.sum +++ b/test/go.sum @@ -52,8 +52,8 @@ github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcK github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= -github.com/miekg/dns v1.1.49 h1:qe0mQU3Z/XpFeE+AEBo2rqaS1IPBJ3anmqZ4XiZJVG8= -github.com/miekg/dns v1.1.49/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= +github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= @@ -78,11 +78,13 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA= github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -95,8 +97,8 @@ go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 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.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 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.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= @@ -115,8 +117,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220617184016-355a448f1bc9 h1:Yqz/iviulwKwAREEeUd3nbBFn0XuyJqkoft2IlrvOhc= -golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220708220712-1185a9018129 h1:vucSRfWwTsoXro7P+3Cjlr6flUMtzCwzlvkxEQtHHB0= +golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 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= @@ -146,8 +148,8 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c h1:aFV+BgZ4svzjfabn8ERpuB4JI4N6/rdy1iusx77G3oU= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e h1:CsOuNlbOuf0mzxJIefr6Q4uAUetRUwZE4qt7VfzP+xo= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= From be8d63ba8f70a6f4e7b240899cd2903a77243c24 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Fri, 15 Jul 2022 17:00:41 +0800 Subject: [PATCH 038/250] Fix: macOS udp find process should use unspecified fallback --- component/process/process_darwin.go | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/component/process/process_darwin.go b/component/process/process_darwin.go index 7870b227..df6f1be8 100644 --- a/component/process/process_darwin.go +++ b/component/process/process_darwin.go @@ -44,6 +44,8 @@ func findProcessName(network string, ip net.IP, port int) (string, error) { // rup8(sizeof(xtcpcb_n)) itemSize += 208 } + + var fallbackUDPProcess string // skip the first xinpgen(24 bytes) block for i := 24; i+itemSize <= len(buf); i += itemSize { // offset of xinpcb_n and xsocket_n @@ -57,11 +59,15 @@ func findProcessName(network string, ip net.IP, port int) (string, error) { // xinpcb_n.inp_vflag flag := buf[inp+44] - var srcIP net.IP + var ( + srcIP net.IP + srcIsIPv4 bool + ) switch { case flag&0x1 > 0 && isIPv4: // ipv4 srcIP = net.IP(buf[inp+76 : inp+80]) + srcIsIPv4 = true case flag&0x2 > 0 && !isIPv4: // ipv6 srcIP = net.IP(buf[inp+64 : inp+80]) @@ -69,13 +75,20 @@ func findProcessName(network string, ip net.IP, port int) (string, error) { continue } - if !ip.Equal(srcIP) { - continue + if ip.Equal(srcIP) { + // xsocket_n.so_last_pid + pid := readNativeUint32(buf[so+68 : so+72]) + return getExecPathFromPID(pid) } - // xsocket_n.so_last_pid - pid := readNativeUint32(buf[so+68 : so+72]) - return getExecPathFromPID(pid) + // udp packet connection may be not equal with srcIP + if network == UDP && srcIP.IsUnspecified() && isIPv4 == srcIsIPv4 { + fallbackUDPProcess, _ = getExecPathFromPID(readNativeUint32(buf[so+68 : so+72])) + } + } + + if network == UDP && fallbackUDPProcess != "" { + return fallbackUDPProcess, nil } return "", ErrNotFound From a79481986945a9700a4b3f91054e74a8ced6dc47 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Thu, 21 Jul 2022 15:15:14 +0800 Subject: [PATCH 039/250] Chore: upgrade actions and fixed golangci-lint version --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/docker.yml | 12 ++++++------ .github/workflows/linter.yml | 12 ++++-------- .github/workflows/release.yml | 10 +++------- 4 files changed, 16 insertions(+), 24 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 8a36dc97..52b6afc8 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -19,12 +19,12 @@ jobs: uses: actions/checkout@v3 - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} - name: Autobuild - uses: github/codeql-action/autobuild@v1 + uses: github/codeql-action/autobuild@v2 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 19fe0d88..d4cba355 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -18,24 +18,24 @@ jobs: fetch-depth: 0 - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v2 with: platforms: all - name: Set up docker buildx id: buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v2 with: version: latest - name: Login to DockerHub - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Login to Github Package - uses: docker/login-action@v1 + uses: docker/login-action@v2 with: registry: ghcr.io username: Dreamacro @@ -43,7 +43,7 @@ jobs: - name: Build dev branch and push if: github.ref == 'refs/heads/dev' - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v3 with: context: . platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64 @@ -70,7 +70,7 @@ jobs: - name: Build release and push if: startsWith(github.ref, 'refs/tags/') - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v3 with: context: . platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64 diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index a81cfc1c..15f6fac7 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -6,17 +6,13 @@ jobs: steps: - uses: actions/checkout@v3 - - name: Get latest go version - id: version - run: | - echo ::set-output name=go_version::$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g') - - name: Setup Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: - go-version: ${{ steps.version.outputs.go_version }} + check-latest: true + go-version: '1.18' - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: - version: latest + version: v1.46.2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 08f97ba3..e1f5d68d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,15 +4,11 @@ jobs: build: runs-on: ubuntu-latest steps: - - name: Get latest go version - id: version - run: | - echo ::set-output name=go_version::$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g') - - name: Setup Go - uses: actions/setup-go@v2 + uses: actions/setup-go@v3 with: - go-version: ${{ steps.version.outputs.go_version }} + check-latest: true + go-version: '1.18' - name: Check out code into the Go module directory uses: actions/checkout@v3 From 93400cf44def4d0ad7c7e8094877d8ce65cfa7d2 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Mon, 25 Jul 2022 12:41:22 +0800 Subject: [PATCH 040/250] Fix: ALPN should on DoH instead of DoT (#2232) --- dns/doh.go | 5 +++++ dns/util.go | 2 -- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/dns/doh.go b/dns/doh.go index 99c22cb1..7e4ed469 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -3,6 +3,7 @@ package dns import ( "bytes" "context" + "crypto/tls" "io" "net" "net/http" @@ -102,6 +103,10 @@ func newDoHClient(url, iface string, r *Resolver) *dohClient { return dialer.DialContext(ctx, "tcp", net.JoinHostPort(ip.String(), port), options...) }, + TLSClientConfig: &tls.Config{ + // alpn identifier, see https://tools.ietf.org/html/draft-hoffman-dprive-dns-tls-alpn-00#page-6 + NextProtos: []string{"dns"}, + }, }, } } diff --git a/dns/util.go b/dns/util.go index d25f5731..99590d63 100644 --- a/dns/util.go +++ b/dns/util.go @@ -63,8 +63,6 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient { Client: &D.Client{ Net: s.Net, TLSConfig: &tls.Config{ - // alpn identifier, see https://tools.ietf.org/html/draft-hoffman-dprive-dns-tls-alpn-00#page-6 - NextProtos: []string{"dns"}, ServerName: host, }, UDPSize: 4096, From bec4df7b122e6a4db0f831ed6176732b2a09fb63 Mon Sep 17 00:00:00 2001 From: Kaming Chan Date: Mon, 25 Jul 2022 12:44:00 +0800 Subject: [PATCH 041/250] Fix: handle parse socks5 udp address properly (#2220) --- transport/snell/snell.go | 3 +++ transport/trojan/trojan.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/transport/snell/snell.go b/transport/snell/snell.go index c6b7a569..e2bd2820 100644 --- a/transport/snell/snell.go +++ b/transport/snell/snell.go @@ -241,6 +241,9 @@ func ReadPacket(r io.Reader, payload []byte) (net.Addr, int, error) { return nil, 0, errors.New("remote address invalid") } uAddr := addr.UDPAddr() + if uAddr == nil { + return nil, 0, errors.New("parse addr error") + } length := len(payload) if n-headLen < length { diff --git a/transport/trojan/trojan.go b/transport/trojan/trojan.go index ac9f17dd..686983b5 100644 --- a/transport/trojan/trojan.go +++ b/transport/trojan/trojan.go @@ -170,6 +170,9 @@ func ReadPacket(r io.Reader, payload []byte) (net.Addr, int, int, error) { return nil, 0, 0, errors.New("read addr error") } uAddr := addr.UDPAddr() + if uAddr == nil { + return nil, 0, 0, errors.New("parse addr error") + } if _, err = io.ReadFull(r, payload[:2]); err != nil { return nil, 0, 0, errors.New("read length error") From 95e3a88608e11984b248eb46fb65ab8556a03b18 Mon Sep 17 00:00:00 2001 From: archzi <261807903@qq.com> Date: Thu, 28 Jul 2022 20:27:53 +0800 Subject: [PATCH 042/250] Chore: update bug_report.yml (#2240) --- .github/ISSUE_TEMPLATE/bug_report.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index b56b9865..aabf9c49 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -56,8 +56,8 @@ This is an issue of the Clash core *per se*, not to the derivatives of Clash, li render: yaml label: "Clash config" description: " -在下方附上 Clash core 脱敏后配置文件的内容 -Paste the Clash core configuration below. +在下方附上 Clash core 配置文件,请确保配置文件中没有敏感信息(比如:服务器地址,密码,端口等) +Paste the Clash core configuration file below, please make sure that there is no sensitive information in the configuration file (e.g., server address/url, password, port) " validations: required: true From 6648793e406ac2151f41f24c2d8c4467d139a81a Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Fri, 5 Aug 2022 10:52:36 +0800 Subject: [PATCH 043/250] Chore: reenable latest golangci-lint --- .github/workflows/linter.yml | 2 +- .golangci.yaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index 15f6fac7..ea07674a 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -15,4 +15,4 @@ jobs: - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: - version: v1.46.2 + version: latest diff --git a/.golangci.yaml b/.golangci.yaml index a0764585..cfe4f07f 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -8,6 +8,7 @@ linters: linters-settings: gci: + custom-order: true sections: - standard - prefix(github.com/Dreamacro/clash) From 50105f0559f514b308e6997163cb6da99ae6b9fe Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Sun, 7 Aug 2022 21:45:50 +0800 Subject: [PATCH 044/250] Migration: go1.19 --- .github/workflows/linter.yml | 2 +- .github/workflows/release.yml | 2 +- .golangci.yaml | 2 +- constant/mime/mime.go | 16 ---------------- hub/route/server.go | 1 - test/.golangci.yaml | 2 +- 6 files changed, 4 insertions(+), 21 deletions(-) delete mode 100644 constant/mime/mime.go diff --git a/.github/workflows/linter.yml b/.github/workflows/linter.yml index ea07674a..f7763147 100644 --- a/.github/workflows/linter.yml +++ b/.github/workflows/linter.yml @@ -10,7 +10,7 @@ jobs: uses: actions/setup-go@v3 with: check-latest: true - go-version: '1.18' + go-version: '1.19' - name: golangci-lint uses: golangci/golangci-lint-action@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e1f5d68d..7d85cebf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,7 +8,7 @@ jobs: uses: actions/setup-go@v3 with: check-latest: true - go-version: '1.18' + go-version: '1.19' - name: Check out code into the Go module directory uses: actions/checkout@v3 diff --git a/.golangci.yaml b/.golangci.yaml index cfe4f07f..f5b67397 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -14,4 +14,4 @@ linters-settings: - prefix(github.com/Dreamacro/clash) - default staticcheck: - go: '1.18' + go: '1.19' diff --git a/constant/mime/mime.go b/constant/mime/mime.go deleted file mode 100644 index 431457be..00000000 --- a/constant/mime/mime.go +++ /dev/null @@ -1,16 +0,0 @@ -package mime - -import ( - "mime" -) - -var consensusMimes = map[string]string{ - // rfc4329: text/javascript is obsolete, so we need to overwrite mime's builtin - ".js": "application/javascript; charset=utf-8", -} - -func init() { - for ext, typ := range consensusMimes { - mime.AddExtensionType(ext, typ) - } -} diff --git a/hub/route/server.go b/hub/route/server.go index d5d8f726..d1f85e53 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -9,7 +9,6 @@ import ( "time" C "github.com/Dreamacro/clash/constant" - _ "github.com/Dreamacro/clash/constant/mime" "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/tunnel/statistic" diff --git a/test/.golangci.yaml b/test/.golangci.yaml index cfaed770..b65afc5e 100644 --- a/test/.golangci.yaml +++ b/test/.golangci.yaml @@ -13,4 +13,4 @@ linters-settings: - prefix(github.com/Dreamacro/clash) - default staticcheck: - go: '1.18' + go: '1.19' From 71cad51e8f95f9e6b1906a60574c591d5776db7b Mon Sep 17 00:00:00 2001 From: bobo liu <7552030+fakeboboliu@users.noreply.github.com> Date: Fri, 12 Aug 2022 13:47:51 +0800 Subject: [PATCH 045/250] Fix: satisfy RFC4343 - DNS case insensitivity (#2260) --- component/fakeip/pool.go | 4 ++++ component/fakeip/pool_test.go | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/component/fakeip/pool.go b/component/fakeip/pool.go index 925882bb..e6730a05 100644 --- a/component/fakeip/pool.go +++ b/component/fakeip/pool.go @@ -3,6 +3,7 @@ package fakeip import ( "errors" "net" + "strings" "sync" "github.com/Dreamacro/clash/common/cache" @@ -36,6 +37,9 @@ type Pool struct { func (p *Pool) Lookup(host string) net.IP { p.mux.Lock() defer p.mux.Unlock() + + // RFC4343: DNS Case Insensitive, we SHOULD return result with all cases. + host = strings.ToLower(host) if ip, exist := p.store.GetByHost(host); exist { return ip } diff --git a/component/fakeip/pool_test.go b/component/fakeip/pool_test.go index bd034636..7e161d03 100644 --- a/component/fakeip/pool_test.go +++ b/component/fakeip/pool_test.go @@ -75,6 +75,27 @@ func TestPool_Basic(t *testing.T) { } } +func TestPool_Case_Insensitive(t *testing.T) { + _, ipnet, _ := net.ParseCIDR("192.168.0.1/29") + pools, tempfile, err := createPools(Options{ + IPNet: ipnet, + Size: 10, + }) + assert.Nil(t, err) + defer os.Remove(tempfile) + + for _, pool := range pools { + first := pool.Lookup("foo.com") + last := pool.Lookup("Foo.Com") + foo, exist := pool.LookBack(last) + + assert.True(t, first.Equal(pool.Lookup("Foo.Com"))) + assert.Equal(t, pool.Lookup("fOo.cOM"), first) + assert.True(t, exist) + assert.Equal(t, foo, "foo.com") + } +} + func TestPool_CycleUsed(t *testing.T) { _, ipnet, _ := net.ParseCIDR("192.168.0.1/29") pools, tempfile, err := createPools(Options{ From 5940f627948b9d2b0ca3e985bff6b28ee63f64c3 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Sat, 13 Aug 2022 12:35:39 +0800 Subject: [PATCH 046/250] Chore: http2 should use DialTLSContext and some tls handshake should with context --- adapter/outbound/http.go | 4 +++- adapter/outbound/socks5.go | 8 ++++++-- go.mod | 4 ++-- go.sum | 8 ++++---- transport/gun/gun.go | 9 ++------- 5 files changed, 17 insertions(+), 16 deletions(-) diff --git a/adapter/outbound/http.go b/adapter/outbound/http.go index 44dc705a..480710dc 100644 --- a/adapter/outbound/http.go +++ b/adapter/outbound/http.go @@ -40,7 +40,9 @@ type HttpOption struct { func (h *Http) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { if h.tlsConfig != nil { cc := tls.Client(c, h.tlsConfig) - err := cc.Handshake() + ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout) + defer cancel() + err := cc.HandshakeContext(ctx) c = cc if err != nil { return nil, fmt.Errorf("%s connect error: %w", h.addr, err) diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go index 398ee3b2..3269457d 100644 --- a/adapter/outbound/socks5.go +++ b/adapter/outbound/socks5.go @@ -39,7 +39,9 @@ type Socks5Option struct { func (ss *Socks5) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { if ss.tls { cc := tls.Client(c, ss.tlsConfig) - err := cc.Handshake() + ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout) + defer cancel() + err := cc.HandshakeContext(ctx) c = cc if err != nil { return nil, fmt.Errorf("%s connect error: %w", ss.addr, err) @@ -87,7 +89,9 @@ func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata, if ss.tls { cc := tls.Client(c, ss.tlsConfig) - err = cc.Handshake() + ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout) + defer cancel() + err = cc.HandshakeContext(ctx) c = cc } diff --git a/go.mod b/go.mod index 42d4d055..55828341 100644 --- a/go.mod +++ b/go.mod @@ -17,9 +17,9 @@ require ( go.uber.org/atomic v1.9.0 go.uber.org/automaxprocs v1.5.1 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d - golang.org/x/net v0.0.0-20220706163947-c90051bbdb60 + golang.org/x/net v0.0.0-20220812174116-3211cb980234 golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f - golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e + golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 9d5eabca..864a9ed6 100644 --- a/go.sum +++ b/go.sum @@ -86,8 +86,8 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220706163947-c90051bbdb60 h1:8NSylCMxLW4JvserAndSgFL7aPli6A68yf0bYFTcWCM= -golang.org/x/net v0.0.0-20220706163947-c90051bbdb60/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E= +golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8= @@ -112,8 +112,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e h1:CsOuNlbOuf0mzxJIefr6Q4uAUetRUwZE4qt7VfzP+xo= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/transport/gun/gun.go b/transport/gun/gun.go index 43988004..faadac74 100644 --- a/transport/gun/gun.go +++ b/transport/gun/gun.go @@ -18,7 +18,6 @@ import ( "time" "github.com/Dreamacro/clash/common/pool" - C "github.com/Dreamacro/clash/constant" "go.uber.org/atomic" "golang.org/x/net/http2" @@ -168,17 +167,13 @@ func (g *Conn) SetDeadline(t time.Time) error { } func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config) *http2.Transport { - dialFunc := func(network, addr string, cfg *tls.Config) (net.Conn, error) { + dialFunc := func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) { pconn, err := dialFn(network, addr) if err != nil { return nil, err } cn := tls.Client(pconn, cfg) - - // fix tls handshake not timeout - ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout) - defer cancel() if err := cn.HandshakeContext(ctx); err != nil { pconn.Close() return nil, err @@ -192,7 +187,7 @@ func NewHTTP2Client(dialFn DialFn, tlsConfig *tls.Config) *http2.Transport { } return &http2.Transport{ - DialTLS: dialFunc, + DialTLSContext: dialFunc, TLSClientConfig: tlsConfig, AllowHTTP: false, DisableCompression: true, From 3946d771e52fb72dfc91543dda8c785597622a9b Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Sat, 13 Aug 2022 13:07:35 +0800 Subject: [PATCH 047/250] Feature: sync missing resolver logic from premium, but still net.IP on opensource --- component/resolver/resolver.go | 83 +++++++++++++++++++++++++--------- dns/client.go | 7 ++- dns/doh.go | 7 ++- dns/resolver.go | 69 +++++++++++++++++++++------- tunnel/tunnel.go | 6 ++- 5 files changed, 129 insertions(+), 43 deletions(-) diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index d10e39cb..daf98a09 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -3,6 +3,7 @@ package resolver import ( "context" "errors" + "fmt" "math/rand" "net" "strings" @@ -33,29 +34,32 @@ var ( ) type Resolver interface { + LookupIP(ctx context.Context, host string) ([]net.IP, error) + LookupIPv4(ctx context.Context, host string) ([]net.IP, error) + LookupIPv6(ctx context.Context, host string) ([]net.IP, error) ResolveIP(host string) (ip net.IP, err error) ResolveIPv4(host string) (ip net.IP, err error) ResolveIPv6(host string) (ip net.IP, err error) } -// ResolveIPv4 with a host, return ipv4 -func ResolveIPv4(host string) (net.IP, error) { +// LookupIPv4 with a host, return ipv4 list +func LookupIPv4(ctx context.Context, host string) ([]net.IP, error) { if node := DefaultHosts.Search(host); node != nil { if ip := node.Data.(net.IP).To4(); ip != nil { - return ip, nil + return []net.IP{ip}, nil } } ip := net.ParseIP(host) if ip != nil { if !strings.Contains(host, ":") { - return ip, nil + return []net.IP{ip}, nil } return nil, ErrIPVersion } if DefaultResolver != nil { - return DefaultResolver.ResolveIPv4(host) + return DefaultResolver.LookupIPv4(ctx, host) } ctx, cancel := context.WithTimeout(context.Background(), DefaultDNSTimeout) @@ -67,31 +71,42 @@ func ResolveIPv4(host string) (net.IP, error) { return nil, ErrIPNotFound } - return ipAddrs[rand.Intn(len(ipAddrs))], nil + return ipAddrs, nil } -// ResolveIPv6 with a host, return ipv6 -func ResolveIPv6(host string) (net.IP, error) { +// ResolveIPv4 with a host, return ipv4 +func ResolveIPv4(host string) (net.IP, error) { + ips, err := LookupIPv4(context.Background(), host) + if err != nil { + return nil, err + } else if len(ips) == 0 { + return nil, fmt.Errorf("%w: %s", ErrIPNotFound, host) + } + return ips[rand.Intn(len(ips))], nil +} + +// LookupIPv6 with a host, return ipv6 list +func LookupIPv6(ctx context.Context, host string) ([]net.IP, error) { if DisableIPv6 { return nil, ErrIPv6Disabled } if node := DefaultHosts.Search(host); node != nil { if ip := node.Data.(net.IP).To16(); ip != nil { - return ip, nil + return []net.IP{ip}, nil } } ip := net.ParseIP(host) if ip != nil { if strings.Contains(host, ":") { - return ip, nil + return []net.IP{ip}, nil } return nil, ErrIPVersion } if DefaultResolver != nil { - return DefaultResolver.ResolveIPv6(host) + return DefaultResolver.LookupIPv6(ctx, host) } ctx, cancel := context.WithTimeout(context.Background(), DefaultDNSTimeout) @@ -103,38 +118,62 @@ func ResolveIPv6(host string) (net.IP, error) { return nil, ErrIPNotFound } - return ipAddrs[rand.Intn(len(ipAddrs))], nil + return ipAddrs, nil } -// ResolveIPWithResolver same as ResolveIP, but with a resolver -func ResolveIPWithResolver(host string, r Resolver) (net.IP, error) { +// ResolveIPv6 with a host, return ipv6 +func ResolveIPv6(host string) (net.IP, error) { + ips, err := LookupIPv6(context.Background(), host) + if err != nil { + return nil, err + } else if len(ips) == 0 { + return nil, fmt.Errorf("%w: %s", ErrIPNotFound, host) + } + return ips[rand.Intn(len(ips))], nil +} + +// LookupIPWithResolver same as ResolveIP, but with a resolver +func LookupIPWithResolver(ctx context.Context, host string, r Resolver) ([]net.IP, error) { if node := DefaultHosts.Search(host); node != nil { - return node.Data.(net.IP), nil + return []net.IP{node.Data.(net.IP)}, nil } if r != nil { if DisableIPv6 { - return r.ResolveIPv4(host) + return r.LookupIPv4(ctx, host) } - return r.ResolveIP(host) + return r.LookupIP(ctx, host) } else if DisableIPv6 { - return ResolveIPv4(host) + return LookupIPv4(ctx, host) } ip := net.ParseIP(host) if ip != nil { - return ip, nil + return []net.IP{ip}, nil } - ipAddr, err := net.ResolveIPAddr("ip", host) + ips, err := net.DefaultResolver.LookupIP(ctx, "ip", host) if err != nil { return nil, err + } else if len(ips) == 0 { + return nil, ErrIPNotFound } - return ipAddr.IP, nil + return ips, nil +} + +// ResolveIP with a host, return ip +func LookupIP(ctx context.Context, host string) ([]net.IP, error) { + return LookupIPWithResolver(ctx, host, DefaultResolver) } // ResolveIP with a host, return ip func ResolveIP(host string) (net.IP, error) { - return ResolveIPWithResolver(host, DefaultResolver) + ips, err := LookupIP(context.Background(), host) + if err != nil { + return nil, err + } else if len(ips) == 0 { + return nil, fmt.Errorf("%w: %s", ErrIPNotFound, host) + } + return ips[rand.Intn(len(ips))], nil } diff --git a/dns/client.go b/dns/client.go index 5cb1fe02..366a179d 100644 --- a/dns/client.go +++ b/dns/client.go @@ -4,6 +4,7 @@ import ( "context" "crypto/tls" "fmt" + "math/rand" "net" "strings" @@ -36,9 +37,13 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) return nil, fmt.Errorf("dns %s not a valid ip", c.host) } } else { - if ip, err = resolver.ResolveIPWithResolver(c.host, c.r); err != nil { + ips, err := resolver.LookupIPWithResolver(ctx, c.host, c.r) + if err != nil { return nil, fmt.Errorf("use default dns resolve failed: %w", err) + } else if len(ips) == 0 { + return nil, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, c.host) } + ip = ips[rand.Intn(len(ips))] } network := "udp" diff --git a/dns/doh.go b/dns/doh.go index 7e4ed469..79820f9c 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -4,7 +4,9 @@ import ( "bytes" "context" "crypto/tls" + "fmt" "io" + "math/rand" "net" "net/http" @@ -91,10 +93,13 @@ func newDoHClient(url, iface string, r *Resolver) *dohClient { return nil, err } - ip, err := resolver.ResolveIPWithResolver(host, r) + ips, err := resolver.LookupIPWithResolver(ctx, host, r) if err != nil { return nil, err + } else if len(ips) == 0 { + return nil, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, host) } + ip := ips[rand.Intn(len(ips))] options := []dialer.Option{} if iface != "" { diff --git a/dns/resolver.go b/dns/resolver.go index cec415ac..5eeda77f 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -42,19 +42,23 @@ type Resolver struct { policy *trie.DomainTrie } -// ResolveIP request with TypeA and TypeAAAA, priority return TypeA -func (r *Resolver) ResolveIP(host string) (ip net.IP, err error) { - ch := make(chan net.IP, 1) +// LookupIP request with TypeA and TypeAAAA, priority return TypeA +func (r *Resolver) LookupIP(ctx context.Context, host string) (ip []net.IP, err error) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + ch := make(chan []net.IP, 1) + go func() { defer close(ch) - ip, err := r.resolveIP(host, D.TypeAAAA) + ip, err := r.lookupIP(ctx, host, D.TypeAAAA) if err != nil { return } ch <- ip }() - ip, err = r.resolveIP(host, D.TypeA) + ip, err = r.lookupIP(ctx, host, D.TypeA) if err == nil { return } @@ -67,14 +71,47 @@ func (r *Resolver) ResolveIP(host string) (ip net.IP, err error) { return ip, nil } +// ResolveIP request with TypeA and TypeAAAA, priority return TypeA +func (r *Resolver) ResolveIP(host string) (ip net.IP, err error) { + ips, err := r.LookupIP(context.Background(), host) + if err != nil { + return nil, err + } else if len(ips) == 0 { + return nil, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, host) + } + return ips[rand.Intn(len(ips))], nil +} + +// LookupIPv4 request with TypeA +func (r *Resolver) LookupIPv4(ctx context.Context, host string) ([]net.IP, error) { + return r.lookupIP(ctx, host, D.TypeA) +} + // ResolveIPv4 request with TypeA func (r *Resolver) ResolveIPv4(host string) (ip net.IP, err error) { - return r.resolveIP(host, D.TypeA) + ips, err := r.lookupIP(context.Background(), host, D.TypeA) + if err != nil { + return nil, err + } else if len(ips) == 0 { + return nil, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, host) + } + return ips[rand.Intn(len(ips))], nil +} + +// LookupIPv6 request with TypeAAAA +func (r *Resolver) LookupIPv6(ctx context.Context, host string) ([]net.IP, error) { + return r.lookupIP(ctx, host, D.TypeAAAA) } // ResolveIPv6 request with TypeAAAA func (r *Resolver) ResolveIPv6(host string) (ip net.IP, err error) { - return r.resolveIP(host, D.TypeAAAA) + ips, err := r.lookupIP(context.Background(), host, D.TypeAAAA) + if err != nil { + return nil, err + } else if len(ips) == 0 { + return nil, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, host) + } + return ips[rand.Intn(len(ips))], nil } func (r *Resolver) shouldIPFallback(ip net.IP) bool { @@ -253,14 +290,15 @@ func (r *Resolver) ipExchange(ctx context.Context, m *D.Msg) (msg *D.Msg, err er return } -func (r *Resolver) resolveIP(host string, dnsType uint16) (ip net.IP, err error) { - ip = net.ParseIP(host) +func (r *Resolver) lookupIP(ctx context.Context, host string, dnsType uint16) ([]net.IP, error) { + ip := net.ParseIP(host) if ip != nil { - isIPv4 := ip.To4() != nil + ip4 := ip.To4() + isIPv4 := ip4 != nil if dnsType == D.TypeAAAA && !isIPv4 { - return ip, nil + return []net.IP{ip}, nil } else if dnsType == D.TypeA && isIPv4 { - return ip, nil + return []net.IP{ip4}, nil } else { return nil, resolver.ErrIPVersion } @@ -275,13 +313,10 @@ func (r *Resolver) resolveIP(host string, dnsType uint16) (ip net.IP, err error) } ips := msgToIP(msg) - ipLength := len(ips) - if ipLength == 0 { + if len(ips) == 0 { return nil, resolver.ErrIPNotFound } - - ip = ips[rand.Intn(ipLength)] - return + return ips, nil } func (r *Resolver) msgToDomain(msg *D.Msg) string { diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 4a4685b2..f5d9ed24 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -180,11 +180,13 @@ func handleUDPConn(packet *inbound.PacketAdapter) { // local resolve UDP dns if !metadata.Resolved() { - ip, err := resolver.ResolveIP(metadata.Host) + ips, err := resolver.LookupIP(context.Background(), metadata.Host) if err != nil { return + } else if len(ips) == 0 { + return } - metadata.DstIP = ip + metadata.DstIP = ips[0] } key := packet.LocalAddr().String() From 6e058f8581ed69057fa7ef2749997fb6f5f73591 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Wed, 17 Aug 2022 11:43:13 +0800 Subject: [PATCH 048/250] Chore: remove old cache implementation --- common/cache/cache.go | 106 ---------------------------------- common/cache/cache_test.go | 70 ---------------------- common/cache/lrucache.go | 4 +- common/cache/lrucache_test.go | 20 +++---- component/fakeip/pool.go | 2 +- dns/enhancer.go | 2 +- dns/resolver.go | 4 +- listener/http/proxy.go | 11 ++-- listener/http/server.go | 5 +- listener/mixed/mixed.go | 7 +-- 10 files changed, 26 insertions(+), 205 deletions(-) delete mode 100644 common/cache/cache.go delete mode 100644 common/cache/cache_test.go diff --git a/common/cache/cache.go b/common/cache/cache.go deleted file mode 100644 index e587d77b..00000000 --- a/common/cache/cache.go +++ /dev/null @@ -1,106 +0,0 @@ -package cache - -import ( - "runtime" - "sync" - "time" -) - -// Cache store element with a expired time -type Cache struct { - *cache -} - -type cache struct { - mapping sync.Map - janitor *janitor -} - -type element struct { - Expired time.Time - Payload any -} - -// Put element in Cache with its ttl -func (c *cache) Put(key any, payload any, ttl time.Duration) { - c.mapping.Store(key, &element{ - Payload: payload, - Expired: time.Now().Add(ttl), - }) -} - -// Get element in Cache, and drop when it expired -func (c *cache) Get(key any) any { - item, exist := c.mapping.Load(key) - if !exist { - return nil - } - elm := item.(*element) - // expired - if time.Since(elm.Expired) > 0 { - c.mapping.Delete(key) - return nil - } - return elm.Payload -} - -// GetWithExpire element in Cache with Expire Time -func (c *cache) GetWithExpire(key any) (payload any, expired time.Time) { - item, exist := c.mapping.Load(key) - if !exist { - return - } - elm := item.(*element) - // expired - if time.Since(elm.Expired) > 0 { - c.mapping.Delete(key) - return - } - return elm.Payload, elm.Expired -} - -func (c *cache) cleanup() { - c.mapping.Range(func(k, v any) bool { - key := k.(string) - elm := v.(*element) - if time.Since(elm.Expired) > 0 { - c.mapping.Delete(key) - } - return true - }) -} - -type janitor struct { - interval time.Duration - stop chan struct{} -} - -func (j *janitor) process(c *cache) { - ticker := time.NewTicker(j.interval) - for { - select { - case <-ticker.C: - c.cleanup() - case <-j.stop: - ticker.Stop() - return - } - } -} - -func stopJanitor(c *Cache) { - c.janitor.stop <- struct{}{} -} - -// New return *Cache -func New(interval time.Duration) *Cache { - j := &janitor{ - interval: interval, - stop: make(chan struct{}), - } - c := &cache{janitor: j} - go j.process(c) - C := &Cache{c} - runtime.SetFinalizer(C, stopJanitor) - return C -} diff --git a/common/cache/cache_test.go b/common/cache/cache_test.go deleted file mode 100644 index cf4a3914..00000000 --- a/common/cache/cache_test.go +++ /dev/null @@ -1,70 +0,0 @@ -package cache - -import ( - "runtime" - "testing" - "time" - - "github.com/stretchr/testify/assert" -) - -func TestCache_Basic(t *testing.T) { - interval := 200 * time.Millisecond - ttl := 20 * time.Millisecond - c := New(interval) - c.Put("int", 1, ttl) - c.Put("string", "a", ttl) - - i := c.Get("int") - assert.Equal(t, i.(int), 1, "should recv 1") - - s := c.Get("string") - assert.Equal(t, s.(string), "a", "should recv 'a'") -} - -func TestCache_TTL(t *testing.T) { - interval := 200 * time.Millisecond - ttl := 20 * time.Millisecond - now := time.Now() - c := New(interval) - c.Put("int", 1, ttl) - c.Put("int2", 2, ttl) - - i := c.Get("int") - _, expired := c.GetWithExpire("int2") - assert.Equal(t, i.(int), 1, "should recv 1") - assert.True(t, now.Before(expired)) - - time.Sleep(ttl * 2) - i = c.Get("int") - j, _ := c.GetWithExpire("int2") - assert.Nil(t, i, "should recv nil") - assert.Nil(t, j, "should recv nil") -} - -func TestCache_AutoCleanup(t *testing.T) { - interval := 10 * time.Millisecond - ttl := 15 * time.Millisecond - c := New(interval) - c.Put("int", 1, ttl) - - time.Sleep(ttl * 2) - i := c.Get("int") - j, _ := c.GetWithExpire("int") - assert.Nil(t, i, "should recv nil") - assert.Nil(t, j, "should recv nil") -} - -func TestCache_AutoGC(t *testing.T) { - sign := make(chan struct{}) - go func() { - interval := 10 * time.Millisecond - ttl := 15 * time.Millisecond - c := New(interval) - c.Put("int", 1, ttl) - sign <- struct{}{} - }() - - <-sign - runtime.GC() -} diff --git a/common/cache/lrucache.go b/common/cache/lrucache.go index 25a1b48e..e0f5d20c 100644 --- a/common/cache/lrucache.go +++ b/common/cache/lrucache.go @@ -64,8 +64,8 @@ type LruCache struct { onEvict EvictCallback } -// NewLRUCache creates an LruCache -func NewLRUCache(options ...Option) *LruCache { +// New creates an LruCache +func New(options ...Option) *LruCache { lc := &LruCache{ lru: list.New(), cache: make(map[any]*list.Element), diff --git a/common/cache/lrucache_test.go b/common/cache/lrucache_test.go index 1a910b4a..1a09975d 100644 --- a/common/cache/lrucache_test.go +++ b/common/cache/lrucache_test.go @@ -19,7 +19,7 @@ var entries = []struct { } func TestLRUCache(t *testing.T) { - c := NewLRUCache() + c := New() for _, e := range entries { c.Set(e.key, e.value) @@ -45,7 +45,7 @@ func TestLRUCache(t *testing.T) { } func TestLRUMaxAge(t *testing.T) { - c := NewLRUCache(WithAge(86400)) + c := New(WithAge(86400)) now := time.Now().Unix() expected := now + 86400 @@ -88,7 +88,7 @@ func TestLRUMaxAge(t *testing.T) { } func TestLRUpdateOnGet(t *testing.T) { - c := NewLRUCache(WithAge(86400), WithUpdateAgeOnGet()) + c := New(WithAge(86400), WithUpdateAgeOnGet()) now := time.Now().Unix() expires := now + 86400/2 @@ -103,7 +103,7 @@ func TestLRUpdateOnGet(t *testing.T) { } func TestMaxSize(t *testing.T) { - c := NewLRUCache(WithSize(2)) + c := New(WithSize(2)) // Add one expired entry c.Set("foo", "bar") _, ok := c.Get("foo") @@ -117,7 +117,7 @@ func TestMaxSize(t *testing.T) { } func TestExist(t *testing.T) { - c := NewLRUCache(WithSize(1)) + c := New(WithSize(1)) c.Set(1, 2) assert.True(t, c.Exist(1)) c.Set(2, 3) @@ -130,7 +130,7 @@ func TestEvict(t *testing.T) { temp = key.(int) + value.(int) } - c := NewLRUCache(WithEvict(evict), WithSize(1)) + c := New(WithEvict(evict), WithSize(1)) c.Set(1, 2) c.Set(2, 3) @@ -138,7 +138,7 @@ func TestEvict(t *testing.T) { } func TestSetWithExpire(t *testing.T) { - c := NewLRUCache(WithAge(1)) + c := New(WithAge(1)) now := time.Now().Unix() tenSecBefore := time.Unix(now-10, 0) @@ -152,7 +152,7 @@ func TestSetWithExpire(t *testing.T) { } func TestStale(t *testing.T) { - c := NewLRUCache(WithAge(1), WithStale(true)) + c := New(WithAge(1), WithStale(true)) now := time.Now().Unix() tenSecBefore := time.Unix(now-10, 0) @@ -165,11 +165,11 @@ func TestStale(t *testing.T) { } func TestCloneTo(t *testing.T) { - o := NewLRUCache(WithSize(10)) + o := New(WithSize(10)) o.Set("1", 1) o.Set("2", 2) - n := NewLRUCache(WithSize(2)) + n := New(WithSize(2)) n.Set("3", 3) n.Set("4", 4) diff --git a/component/fakeip/pool.go b/component/fakeip/pool.go index e6730a05..1d39b112 100644 --- a/component/fakeip/pool.go +++ b/component/fakeip/pool.go @@ -168,7 +168,7 @@ func New(options Options) (*Pool, error) { } } else { pool.store = &memoryStore{ - cache: cache.NewLRUCache(cache.WithSize(options.Size * 2)), + cache: cache.New(cache.WithSize(options.Size * 2)), } } diff --git a/dns/enhancer.go b/dns/enhancer.go index 76f0f262..16ae0202 100644 --- a/dns/enhancer.go +++ b/dns/enhancer.go @@ -78,7 +78,7 @@ func NewEnhancer(cfg Config) *ResolverEnhancer { if cfg.EnhancedMode != C.DNSNormal { fakePool = cfg.Pool - mapping = cache.NewLRUCache(cache.WithSize(4096), cache.WithStale(true)) + mapping = cache.New(cache.WithSize(4096), cache.WithStale(true)) } return &ResolverEnhancer{ diff --git a/dns/resolver.go b/dns/resolver.go index 5eeda77f..2f993600 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -363,13 +363,13 @@ type Config struct { func NewResolver(config Config) *Resolver { defaultResolver := &Resolver{ main: transform(config.Default, nil), - lruCache: cache.NewLRUCache(cache.WithSize(4096), cache.WithStale(true)), + lruCache: cache.New(cache.WithSize(4096), cache.WithStale(true)), } r := &Resolver{ ipv6: config.IPv6, main: transform(config.Main, defaultResolver), - lruCache: cache.NewLRUCache(cache.WithSize(4096), cache.WithStale(true)), + lruCache: cache.New(cache.WithSize(4096), cache.WithStale(true)), hosts: config.Hosts, } diff --git a/listener/http/proxy.go b/listener/http/proxy.go index 32701e62..989d1196 100644 --- a/listener/http/proxy.go +++ b/listener/http/proxy.go @@ -5,7 +5,6 @@ import ( "net" "net/http" "strings" - "time" "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/common/cache" @@ -15,7 +14,7 @@ import ( "github.com/Dreamacro/clash/log" ) -func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.Cache) { +func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.LruCache) { client := newClient(c.RemoteAddr(), in) defer client.CloseIdleConnections() @@ -99,7 +98,7 @@ func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.Cache) { conn.Close() } -func authenticate(request *http.Request, cache *cache.Cache) *http.Response { +func authenticate(request *http.Request, cache *cache.LruCache) *http.Response { authenticator := authStore.Authenticator() if authenticator != nil { credential := parseBasicProxyAuthorization(request) @@ -109,11 +108,11 @@ func authenticate(request *http.Request, cache *cache.Cache) *http.Response { return resp } - var authed any - if authed = cache.Get(credential); authed == nil { + authed, exist := cache.Get(credential) + if !exist { user, pass, err := decodeBasicProxyAuthorization(credential) authed = err == nil && authenticator.Verify(user, pass) - cache.Put(credential, authed, time.Minute) + cache.Set(credential, authed) } if !authed.(bool) { log.Infoln("Auth failed from %s", request.RemoteAddr) diff --git a/listener/http/server.go b/listener/http/server.go index bfdd9f1b..1c21cd63 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -2,7 +2,6 @@ package http import ( "net" - "time" "github.com/Dreamacro/clash/common/cache" C "github.com/Dreamacro/clash/constant" @@ -40,9 +39,9 @@ func NewWithAuthenticate(addr string, in chan<- C.ConnContext, authenticate bool return nil, err } - var c *cache.Cache + var c *cache.LruCache if authenticate { - c = cache.New(time.Second * 30) + c = cache.New(cache.WithAge(30)) } hl := &Listener{ diff --git a/listener/mixed/mixed.go b/listener/mixed/mixed.go index 57fd055e..f720e49f 100644 --- a/listener/mixed/mixed.go +++ b/listener/mixed/mixed.go @@ -2,7 +2,6 @@ package mixed import ( "net" - "time" "github.com/Dreamacro/clash/common/cache" N "github.com/Dreamacro/clash/common/net" @@ -16,7 +15,7 @@ import ( type Listener struct { listener net.Listener addr string - cache *cache.Cache + cache *cache.LruCache closed bool } @@ -45,7 +44,7 @@ func New(addr string, in chan<- C.ConnContext) (*Listener, error) { ml := &Listener{ listener: l, addr: addr, - cache: cache.New(30 * time.Second), + cache: cache.New(cache.WithAge(30)), } go func() { for { @@ -63,7 +62,7 @@ func New(addr string, in chan<- C.ConnContext) (*Listener, error) { return ml, nil } -func handleConn(conn net.Conn, in chan<- C.ConnContext, cache *cache.Cache) { +func handleConn(conn net.Conn, in chan<- C.ConnContext, cache *cache.LruCache) { conn.(*net.TCPConn).SetKeepAlive(true) bufConn := N.NewBufferedConn(conn) From bf079742cb9015cf733137fb0a9dfc2ebb069dea Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Wed, 24 Aug 2022 20:20:34 +0800 Subject: [PATCH 049/250] Clean: use go 1.19 Appendf --- component/process/process_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/component/process/process_linux.go b/component/process/process_linux.go index 1dfd0eda..158a353c 100644 --- a/component/process/process_linux.go +++ b/component/process/process_linux.go @@ -166,7 +166,7 @@ func resolveProcessNameByProcSearch(inode, uid int32) (string, error) { } buffer := make([]byte, syscall.PathMax) - socket := []byte(fmt.Sprintf("socket:[%d]", inode)) + socket := fmt.Appendf(nil, "socket:[%d]", inode) for _, f := range files { if !f.IsDir() || !isPid(f.Name()) { From a3281712e2b953b900233ec8753c37b5356d8055 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Wed, 24 Aug 2022 21:36:19 +0800 Subject: [PATCH 050/250] Chore: reduce dhcp dns client cost --- dns/dhcp.go | 19 +++++++++---------- dns/resolver.go | 28 +++------------------------- dns/util.go | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 35 deletions(-) diff --git a/dns/dhcp.go b/dns/dhcp.go index f964cec8..8e0d5d4c 100644 --- a/dns/dhcp.go +++ b/dns/dhcp.go @@ -29,7 +29,7 @@ type dhcpClient struct { ifaceAddr *net.IPNet done chan struct{} - resolver *Resolver + clients []dnsClient err error } @@ -41,15 +41,15 @@ func (d *dhcpClient) Exchange(m *D.Msg) (msg *D.Msg, err error) { } func (d *dhcpClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { - res, err := d.resolve(ctx) + clients, err := d.resolve(ctx) if err != nil { return nil, err } - return res.ExchangeContext(ctx, m) + return batchExchange(ctx, clients, m) } -func (d *dhcpClient) resolve(ctx context.Context) (*Resolver, error) { +func (d *dhcpClient) resolve(ctx context.Context) ([]dnsClient, error) { d.lock.Lock() invalidated, err := d.invalidate() @@ -64,8 +64,9 @@ func (d *dhcpClient) resolve(ctx context.Context) (*Resolver, error) { ctx, cancel := context.WithTimeout(context.Background(), DHCPTimeout) defer cancel() - var res *Resolver + var res []dnsClient dns, err := dhcp.ResolveDNSFromDHCP(ctx, d.ifaceName) + // dns never empty if err is nil if err == nil { nameserver := make([]NameServer, 0, len(dns)) for _, item := range dns { @@ -75,9 +76,7 @@ func (d *dhcpClient) resolve(ctx context.Context) (*Resolver, error) { }) } - res = NewResolver(Config{ - Main: nameserver, - }) + res = transform(nameserver, nil) } d.lock.Lock() @@ -86,7 +85,7 @@ func (d *dhcpClient) resolve(ctx context.Context) (*Resolver, error) { close(done) d.done = nil - d.resolver = res + d.clients = res d.err = err }() } @@ -96,7 +95,7 @@ func (d *dhcpClient) resolve(ctx context.Context) (*Resolver, error) { for { d.lock.Lock() - res, err, done := d.resolver, d.err, d.done + res, err, done := d.clients, d.err, d.done d.lock.Unlock() diff --git a/dns/resolver.go b/dns/resolver.go index 2f993600..4d3634a4 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -10,7 +10,6 @@ import ( "time" "github.com/Dreamacro/clash/common/cache" - "github.com/Dreamacro/clash/common/picker" "github.com/Dreamacro/clash/component/fakeip" "github.com/Dreamacro/clash/component/resolver" "github.com/Dreamacro/clash/component/trie" @@ -187,31 +186,10 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M } func (r *Resolver) batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) { - fast, ctx := picker.WithTimeout(ctx, resolver.DefaultDNSTimeout) - for _, client := range clients { - r := client - fast.Go(func() (any, error) { - m, err := r.ExchangeContext(ctx, m) - if err != nil { - return nil, err - } else if m.Rcode == D.RcodeServerFailure || m.Rcode == D.RcodeRefused { - return nil, errors.New("server failure") - } - return m, nil - }) - } + ctx, cancel := context.WithTimeout(ctx, resolver.DefaultDNSTimeout) + defer cancel() - elm := fast.Wait() - if elm == nil { - err := errors.New("all DNS requests failed") - if fErr := fast.Error(); fErr != nil { - err = fmt.Errorf("%w, first error: %s", err, fErr.Error()) - } - return nil, err - } - - msg = elm.(*D.Msg) - return + return batchExchange(ctx, clients, m) } func (r *Resolver) matchPolicy(m *D.Msg) []dnsClient { diff --git a/dns/util.go b/dns/util.go index 99590d63..df3a3331 100644 --- a/dns/util.go +++ b/dns/util.go @@ -1,11 +1,15 @@ package dns import ( + "context" "crypto/tls" + "errors" + "fmt" "net" "time" "github.com/Dreamacro/clash/common/cache" + "github.com/Dreamacro/clash/common/picker" "github.com/Dreamacro/clash/log" D "github.com/miekg/dns" @@ -102,3 +106,31 @@ func msgToIP(msg *D.Msg) []net.IP { return ips } + +func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) { + fast, ctx := picker.WithContext(ctx) + for _, client := range clients { + r := client + fast.Go(func() (any, error) { + m, err := r.ExchangeContext(ctx, m) + if err != nil { + return nil, err + } else if m.Rcode == D.RcodeServerFailure || m.Rcode == D.RcodeRefused { + return nil, errors.New("server failure") + } + return m, nil + }) + } + + elm := fast.Wait() + if elm == nil { + err := errors.New("all DNS requests failed") + if fErr := fast.Error(); fErr != nil { + err = fmt.Errorf("%w, first error: %s", err, fErr.Error()) + } + return nil, err + } + + msg = elm.(*D.Msg) + return +} From 2516169f61c03ba3a5076a56b2e3c29a381df5a5 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Fri, 26 Aug 2022 21:18:16 +0800 Subject: [PATCH 051/250] Chore: update dependencies --- go.mod | 21 +++++++++++---------- go.sum | 47 ++++++++++++++++++++++++----------------------- 2 files changed, 35 insertions(+), 33 deletions(-) diff --git a/go.mod b/go.mod index 55828341..a6fe15e0 100644 --- a/go.mod +++ b/go.mod @@ -5,28 +5,29 @@ go 1.18 require ( github.com/go-chi/chi/v5 v5.0.7 github.com/go-chi/cors v1.2.1 - github.com/go-chi/render v1.0.1 + github.com/go-chi/render v1.0.2 github.com/gofrs/uuid v4.2.0+incompatible github.com/gorilla/websocket v1.5.0 - github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f + github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84 github.com/miekg/dns v1.1.50 - github.com/oschwald/geoip2-golang v1.7.0 - github.com/sirupsen/logrus v1.8.1 + github.com/oschwald/geoip2-golang v1.8.0 + github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.0 go.etcd.io/bbolt v1.3.6 - go.uber.org/atomic v1.9.0 + go.uber.org/atomic v1.10.0 go.uber.org/automaxprocs v1.5.1 - golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d - golang.org/x/net v0.0.0-20220812174116-3211cb980234 - golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f - golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 + golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503 + golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c + golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde + golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 gopkg.in/yaml.v3 v3.0.1 ) require ( + github.com/ajg/form v1.5.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/kr/text v0.2.0 // indirect - github.com/oschwald/maxminddb-golang v1.9.0 // indirect + github.com/oschwald/maxminddb-golang v1.10.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect golang.org/x/mod v0.4.2 // indirect diff --git a/go.sum b/go.sum index 864a9ed6..a86cdaa7 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= +github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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= @@ -7,8 +9,8 @@ github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8= github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= -github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8= -github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns= +github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg= +github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -20,8 +22,8 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= -github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f h1:l1QCwn715k8nYkj4Ql50rzEog3WnMdrd4YYMMwemxEo= -github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= +github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84 h1:MJTy6H+EpXLeAn0P5WAWeLk6dJA3V0ik6S3VJfUyQuI= +github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= @@ -39,22 +41,21 @@ github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZ github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/oschwald/geoip2-golang v1.7.0 h1:JW1r5AKi+vv2ujSxjKthySK3jo8w8oKWPyXsw+Qs/S8= -github.com/oschwald/geoip2-golang v1.7.0/go.mod h1:mdI/C7iK7NVMcIDDtf4bCKMJ7r0o7UwGeCo9eiitCMQ= -github.com/oschwald/maxminddb-golang v1.9.0 h1:tIk4nv6VT9OiPyrnDAfJS1s1xKDQMZOsGojab6EjC1Y= -github.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm82Cp5HyvYbt8K3zLY= +github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs= +github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw= +github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg= +github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -63,15 +64,15 @@ github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/automaxprocs v1.5.1 h1:e1YG66Lrk73dn4qhg8WFSvhF0JuFQF0ERIp4rpuV8Qk= go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU= 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.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503 h1:vJ2V3lFLg+bBhgroYuRfyN583UzVveQmIXjc8T/y3to= +golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -86,12 +87,12 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E= -golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c h1:JVAXQ10yGGVbSyoer5VILysz6YKjdNT2bsvlayjqhes= +golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -100,7 +101,6 @@ golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -112,8 +112,9 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM= +golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= From 425b6e0dc098535874a29ccc45a5c5ebece324ce Mon Sep 17 00:00:00 2001 From: Birkhoff Lee Date: Sat, 27 Aug 2022 12:16:25 +0800 Subject: [PATCH 052/250] Chore: update README (#2276) --- README.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index c0d7c7f7..73af1a65 100644 --- a/README.md +++ b/README.md @@ -24,28 +24,28 @@ ## Features - Local HTTP/HTTPS/SOCKS server with authentication support -- VMess, Shadowsocks, Trojan, Snell protocol support for remote connections -- Built-in DNS server that aims to minimize DNS pollution attack impact, supports DoH/DoT upstream and fake IP. -- Rules based off domains, GEOIP, IPCIDR or Process to forward packets to different nodes -- Remote groups allow users to implement powerful rules. Supports automatic fallback, load balancing or auto select node based off latency -- Remote providers, allowing users to get node lists remotely instead of hardcoding in config -- Netfilter TCP redirecting. Deploy Clash on your Internet gateway with `iptables`. -- Comprehensive HTTP RESTful API controller +- Shadowsocks(R), VMess, Trojan, Snell, SOCKS5, HTTP(S) outbound support +- Built-in [fake-ip](https://www.rfc-editor.org/rfc/rfc3089) DNS server that aims to minimize DNS pollution attack impact. DoH/DoT upstream supported. +- Rules based off domains, GEOIP, IP-CIDR or process names to route packets to different destinations +- Proxy groups allow users to implement powerful rules. Supports automatic fallback, load balancing or auto select proxy based off latency +- Remote providers, allowing users to get proxy lists remotely instead of hardcoding in config +- Transparent proxy: Redirect TCP and TProxy TCP/UDP with automatic route table/rule management +- Hot-reload via the comprehensive HTTP RESTful API controller -## Premium Features +## Premium -- TUN mode on macOS, Linux and Windows. [Doc](https://github.com/Dreamacro/clash/wiki/premium-core-features#tun-device) -- Match your tunnel by [Script](https://github.com/Dreamacro/clash/wiki/premium-core-features#script) -- [Rule Provider](https://github.com/Dreamacro/clash/wiki/premium-core-features#rule-providers) +Premium core is proprietary. You can find their release notes and pre-built binaries [here](https://github.com/Dreamacro/clash/releases/tag/premium). + +- gvisor/system stack TUN device on macOS, Linux and Windows ([ref](https://github.com/Dreamacro/clash/wiki/premium-core-features#tun-device)) +- Policy routing with [Scripts](https://github.com/Dreamacro/clash/wiki/premium-core-features#script) +- Load your rules with [Rule Providers](https://github.com/Dreamacro/clash/wiki/premium-core-features#rule-providers) +- Monitor Clash usage with a built-in profiling engine. ([Dreamacro/clash-tracing](https://github.com/Dreamacro/clash-tracing)) ## Getting Started -Documentations are now moved to [GitHub Wiki](https://github.com/Dreamacro/clash/wiki). - -## Premium Release -[Release](https://github.com/Dreamacro/clash/releases/tag/premium) +Documentations are available at [GitHub Wiki](https://github.com/Dreamacro/clash/wiki). ## Development -If you want to build an application that uses clash as a library, check out the the [GitHub Wiki](https://github.com/Dreamacro/clash/wiki/use-clash-as-a-library) +If you want to build a Go application that uses Clash as a library, check out the [GitHub Wiki](https://github.com/Dreamacro/clash/wiki/Using-Clash-in-your-Golang-program). ## Credits From 22b9befbda9ae6d7f1b302aac61b3627db57a5c8 Mon Sep 17 00:00:00 2001 From: Kr328 Date: Thu, 1 Sep 2022 11:33:47 +0800 Subject: [PATCH 053/250] Fix: fake ip pool offset calculate (#2281) --- component/fakeip/pool.go | 16 ++++++++-------- component/fakeip/pool_test.go | 17 +++++++---------- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/component/fakeip/pool.go b/component/fakeip/pool.go index 1d39b112..fa7fba05 100644 --- a/component/fakeip/pool.go +++ b/component/fakeip/pool.go @@ -21,7 +21,7 @@ type store interface { CloneTo(store) } -// Pool is a implementation about fake ip generator without storage +// Pool is an implementation about fake ip generator without storage type Pool struct { max uint32 min uint32 @@ -99,21 +99,21 @@ func (p *Pool) CloneFrom(o *Pool) { func (p *Pool) get(host string) net.IP { current := p.offset for { + ip := uintToIP(p.min + p.offset) + if !p.store.Exist(ip) { + break + } + p.offset = (p.offset + 1) % (p.max - p.min) // Avoid infinite loops if p.offset == current { p.offset = (p.offset + 1) % (p.max - p.min) - ip := uintToIP(p.min + p.offset - 1) + ip := uintToIP(p.min + p.offset) p.store.DelByIP(ip) break } - - ip := uintToIP(p.min + p.offset - 1) - if !p.store.Exist(ip) { - break - } } - ip := uintToIP(p.min + p.offset - 1) + ip := uintToIP(p.min + p.offset) p.store.PutByIP(ip, host) return ip } diff --git a/component/fakeip/pool_test.go b/component/fakeip/pool_test.go index 7e161d03..e5b7ec5d 100644 --- a/component/fakeip/pool_test.go +++ b/component/fakeip/pool_test.go @@ -1,7 +1,6 @@ package fakeip import ( - "fmt" "net" "os" "testing" @@ -106,15 +105,13 @@ func TestPool_CycleUsed(t *testing.T) { defer os.Remove(tempfile) for _, pool := range pools { - foo := pool.Lookup("foo.com") - bar := pool.Lookup("bar.com") - for i := 0; i < 3; i++ { - pool.Lookup(fmt.Sprintf("%d.com", i)) - } - baz := pool.Lookup("baz.com") - next := pool.Lookup("foo.com") - assert.True(t, foo.Equal(baz)) - assert.True(t, next.Equal(bar)) + assert.Equal(t, net.IP{192, 168, 0, 2}, pool.Lookup("2.com")) + assert.Equal(t, net.IP{192, 168, 0, 3}, pool.Lookup("3.com")) + assert.Equal(t, net.IP{192, 168, 0, 4}, pool.Lookup("4.com")) + assert.Equal(t, net.IP{192, 168, 0, 5}, pool.Lookup("5.com")) + assert.Equal(t, net.IP{192, 168, 0, 6}, pool.Lookup("6.com")) + assert.Equal(t, net.IP{192, 168, 0, 2}, pool.Lookup("12.com")) + assert.Equal(t, net.IP{192, 168, 0, 3}, pool.Lookup("3.com")) } } From 4f291fa513509ed7907f64e5eaba7058716a5950 Mon Sep 17 00:00:00 2001 From: x2c3z4 Date: Fri, 2 Sep 2022 16:59:00 +0800 Subject: [PATCH 054/250] Chore: show the source ip in log (#2284) Co-authored-by: Li Feng --- tunnel/tunnel.go | 50 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index f5d9ed24..0936056a 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -233,9 +233,15 @@ func handleUDPConn(packet *inbound.PacketAdapter) { rawPc, err := proxy.ListenPacketContext(ctx, metadata.Pure()) if err != nil { if rule == nil { - log.Warnln("[UDP] dial %s to %s error: %s", proxy.Name(), metadata.RemoteAddress(), err.Error()) + log.Warnln( + "[UDP] dial %s %s --> %s error: %s", + proxy.Name(), + metadata.SourceAddress(), + metadata.RemoteAddress(), + err.Error(), + ) } else { - log.Warnln("[UDP] dial %s (match %s/%s) to %s error: %s", proxy.Name(), rule.RuleType().String(), rule.Payload(), metadata.RemoteAddress(), err.Error()) + log.Warnln("[UDP] dial %s (match %s/%s) %s --> %s error: %s", proxy.Name(), rule.RuleType().String(), rule.Payload(), metadata.SourceAddress(), metadata.RemoteAddress(), err.Error()) } return } @@ -244,13 +250,24 @@ func handleUDPConn(packet *inbound.PacketAdapter) { switch true { case rule != nil: - log.Infoln("[UDP] %s --> %s match %s(%s) using %s", metadata.SourceAddress(), metadata.RemoteAddress(), rule.RuleType().String(), rule.Payload(), rawPc.Chains().String()) + log.Infoln( + "[UDP] %s --> %s match %s(%s) using %s", + metadata.SourceAddress(), + metadata.RemoteAddress(), + rule.RuleType().String(), + rule.Payload(), + rawPc.Chains().String(), + ) case mode == Global: log.Infoln("[UDP] %s --> %s using GLOBAL", metadata.SourceAddress(), metadata.RemoteAddress()) case mode == Direct: log.Infoln("[UDP] %s --> %s using DIRECT", metadata.SourceAddress(), metadata.RemoteAddress()) default: - log.Infoln("[UDP] %s --> %s doesn't match any rule using DIRECT", metadata.SourceAddress(), metadata.RemoteAddress()) + log.Infoln( + "[UDP] %s --> %s doesn't match any rule using DIRECT", + metadata.SourceAddress(), + metadata.RemoteAddress(), + ) } oAddr, _ := netip.AddrFromSlice(metadata.DstIP) @@ -287,9 +304,15 @@ func handleTCPConn(connCtx C.ConnContext) { remoteConn, err := proxy.DialContext(ctx, metadata.Pure()) if err != nil { if rule == nil { - log.Warnln("[TCP] dial %s to %s error: %s", proxy.Name(), metadata.RemoteAddress(), err.Error()) + log.Warnln( + "[TCP] dial %s %s --> %s error: %s", + proxy.Name(), + metadata.SourceAddress(), + metadata.RemoteAddress(), + err.Error(), + ) } else { - log.Warnln("[TCP] dial %s (match %s/%s) to %s error: %s", proxy.Name(), rule.RuleType().String(), rule.Payload(), metadata.RemoteAddress(), err.Error()) + log.Warnln("[TCP] dial %s (match %s/%s) %s --> %s error: %s", proxy.Name(), rule.RuleType().String(), rule.Payload(), metadata.SourceAddress(), metadata.RemoteAddress(), err.Error()) } return } @@ -298,13 +321,24 @@ func handleTCPConn(connCtx C.ConnContext) { switch true { case rule != nil: - log.Infoln("[TCP] %s --> %s match %s(%s) using %s", metadata.SourceAddress(), metadata.RemoteAddress(), rule.RuleType().String(), rule.Payload(), remoteConn.Chains().String()) + log.Infoln( + "[TCP] %s --> %s match %s(%s) using %s", + metadata.SourceAddress(), + metadata.RemoteAddress(), + rule.RuleType().String(), + rule.Payload(), + remoteConn.Chains().String(), + ) case mode == Global: log.Infoln("[TCP] %s --> %s using GLOBAL", metadata.SourceAddress(), metadata.RemoteAddress()) case mode == Direct: log.Infoln("[TCP] %s --> %s using DIRECT", metadata.SourceAddress(), metadata.RemoteAddress()) default: - log.Infoln("[TCP] %s --> %s doesn't match any rule using DIRECT", metadata.SourceAddress(), metadata.RemoteAddress()) + log.Infoln( + "[TCP] %s --> %s doesn't match any rule using DIRECT", + metadata.SourceAddress(), + metadata.RemoteAddress(), + ) } handleSocket(connCtx, remoteConn) From 6995e9818177d65e9da7964168fc489ad0ed549d Mon Sep 17 00:00:00 2001 From: Kr328 Date: Sun, 18 Sep 2022 12:53:51 +0800 Subject: [PATCH 055/250] Refactor: linux process resolving (#2305) --- component/process/process_linux.go | 215 ++++++++++++----------------- go.mod | 4 + go.sum | 16 +++ 3 files changed, 111 insertions(+), 124 deletions(-) diff --git a/component/process/process_linux.go b/component/process/process_linux.go index 158a353c..1edd486c 100644 --- a/component/process/process_linux.go +++ b/component/process/process_linux.go @@ -6,31 +6,57 @@ import ( "fmt" "net" "os" - "path" + "path/filepath" "strings" "syscall" "unicode" "unsafe" - "github.com/Dreamacro/clash/common/pool" + "github.com/mdlayher/netlink" + "golang.org/x/sys/unix" ) -// from https://github.com/vishvananda/netlink/blob/bca67dfc8220b44ef582c9da4e9172bf1c9ec973/nl/nl_linux.go#L52-L62 -var nativeEndian = func() binary.ByteOrder { - var x uint32 = 0x01020304 - if *(*byte)(unsafe.Pointer(&x)) == 0x01 { - return binary.BigEndian - } - - return binary.LittleEndian -}() - const ( - sizeOfSocketDiagRequest = syscall.SizeofNlMsghdr + 8 + 48 - socketDiagByFamily = 20 - pathProc = "/proc" + SOCK_DIAG_BY_FAMILY = 20 + inetDiagRequestSize = int(unsafe.Sizeof(inetDiagRequest{})) + inetDiagResponseSize = int(unsafe.Sizeof(inetDiagResponse{})) ) +type inetDiagRequest struct { + Family byte + Protocol byte + Ext byte + Pad byte + States uint32 + + SrcPort [2]byte + DstPort [2]byte + Src [16]byte + Dst [16]byte + If uint32 + Cookie [2]uint32 +} + +type inetDiagResponse struct { + Family byte + State byte + Timer byte + ReTrans byte + + SrcPort [2]byte + DstPort [2]byte + Src [16]byte + Dst [16]byte + If uint32 + Cookie [2]uint32 + + Expires uint32 + RQueue uint32 + WQueue uint32 + UID uint32 + INode uint32 +} + func findProcessName(network string, ip net.IP, srcPort int) (string, error) { inode, uid, err := resolveSocketByNetlink(network, ip, srcPort) if err != nil { @@ -40,132 +66,73 @@ func findProcessName(network string, ip net.IP, srcPort int) (string, error) { return resolveProcessNameByProcSearch(inode, uid) } -func resolveSocketByNetlink(network string, ip net.IP, srcPort int) (int32, int32, error) { - var family byte - var protocol byte - - switch network { - case TCP: - protocol = syscall.IPPROTO_TCP - case UDP: - protocol = syscall.IPPROTO_UDP - default: - return 0, 0, ErrInvalidNetwork +func resolveSocketByNetlink(network string, ip net.IP, srcPort int) (uint32, uint32, error) { + request := &inetDiagRequest{ + States: 0xffffffff, + Cookie: [2]uint32{0xffffffff, 0xffffffff}, } if ip.To4() != nil { - family = syscall.AF_INET + request.Family = unix.AF_INET } else { - family = syscall.AF_INET6 + request.Family = unix.AF_INET6 } - req := packSocketDiagRequest(family, protocol, ip, uint16(srcPort)) + if strings.HasPrefix(network, "tcp") { + request.Protocol = unix.IPPROTO_TCP + } else if strings.HasPrefix(network, "udp") { + request.Protocol = unix.IPPROTO_UDP + } else { + return 0, 0, ErrInvalidNetwork + } - socket, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_DGRAM, syscall.NETLINK_INET_DIAG) + if v4 := ip.To4(); v4 != nil { + copy(request.Src[:], v4) + } else { + copy(request.Src[:], ip) + } + + binary.BigEndian.PutUint16(request.SrcPort[:], uint16(srcPort)) + + conn, err := netlink.Dial(unix.NETLINK_INET_DIAG, nil) if err != nil { - return 0, 0, fmt.Errorf("dial netlink: %w", err) + return 0, 0, err } - defer syscall.Close(socket) + defer conn.Close() - syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, &syscall.Timeval{Usec: 100}) - syscall.SetsockoptTimeval(socket, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, &syscall.Timeval{Usec: 100}) + message := netlink.Message{ + Header: netlink.Header{ + Type: SOCK_DIAG_BY_FAMILY, + Flags: netlink.Request | netlink.Dump, + }, + Data: (*(*[inetDiagRequestSize]byte)(unsafe.Pointer(request)))[:], + } - if err := syscall.Connect(socket, &syscall.SockaddrNetlink{ - Family: syscall.AF_NETLINK, - Pad: 0, - Pid: 0, - Groups: 0, - }); err != nil { + messages, err := conn.Execute(message) + if err != nil { return 0, 0, err } - if _, err := syscall.Write(socket, req); err != nil { - return 0, 0, fmt.Errorf("write request: %w", err) + for _, msg := range messages { + if len(msg.Data) < inetDiagResponseSize { + continue + } + + response := (*inetDiagResponse)(unsafe.Pointer(&msg.Data[0])) + + return response.INode, response.UID, nil } - rb := pool.Get(pool.RelayBufferSize) - defer pool.Put(rb) - - n, err := syscall.Read(socket, rb) - if err != nil { - return 0, 0, fmt.Errorf("read response: %w", err) - } - - messages, err := syscall.ParseNetlinkMessage(rb[:n]) - if err != nil { - return 0, 0, fmt.Errorf("parse netlink message: %w", err) - } else if len(messages) == 0 { - return 0, 0, fmt.Errorf("unexcepted netlink response") - } - - message := messages[0] - if message.Header.Type&syscall.NLMSG_ERROR != 0 { - return 0, 0, fmt.Errorf("netlink message: NLMSG_ERROR") - } - - inode, uid := unpackSocketDiagResponse(&messages[0]) - if inode < 0 || uid < 0 { - return 0, 0, fmt.Errorf("invalid inode(%d) or uid(%d)", inode, uid) - } - - return inode, uid, nil + return 0, 0, ErrNotFound } -func packSocketDiagRequest(family, protocol byte, source net.IP, sourcePort uint16) []byte { - s := make([]byte, 16) - - if v4 := source.To4(); v4 != nil { - copy(s, v4) - } else { - copy(s, source) - } - - buf := make([]byte, sizeOfSocketDiagRequest) - - nativeEndian.PutUint32(buf[0:4], sizeOfSocketDiagRequest) - nativeEndian.PutUint16(buf[4:6], socketDiagByFamily) - nativeEndian.PutUint16(buf[6:8], syscall.NLM_F_REQUEST|syscall.NLM_F_DUMP) - nativeEndian.PutUint32(buf[8:12], 0) - nativeEndian.PutUint32(buf[12:16], 0) - - buf[16] = family - buf[17] = protocol - buf[18] = 0 - buf[19] = 0 - nativeEndian.PutUint32(buf[20:24], 0xFFFFFFFF) - - binary.BigEndian.PutUint16(buf[24:26], sourcePort) - binary.BigEndian.PutUint16(buf[26:28], 0) - - copy(buf[28:44], s) - copy(buf[44:60], net.IPv6zero) - - nativeEndian.PutUint32(buf[60:64], 0) - nativeEndian.PutUint64(buf[64:72], 0xFFFFFFFFFFFFFFFF) - - return buf -} - -func unpackSocketDiagResponse(msg *syscall.NetlinkMessage) (inode, uid int32) { - if len(msg.Data) < 72 { - return 0, 0 - } - - data := msg.Data - - uid = int32(nativeEndian.Uint32(data[64:68])) - inode = int32(nativeEndian.Uint32(data[68:72])) - - return -} - -func resolveProcessNameByProcSearch(inode, uid int32) (string, error) { - files, err := os.ReadDir(pathProc) +func resolveProcessNameByProcSearch(inode, uid uint32) (string, error) { + files, err := os.ReadDir("/proc") if err != nil { return "", err } - buffer := make([]byte, syscall.PathMax) + buffer := make([]byte, unix.PathMax) socket := fmt.Appendf(nil, "socket:[%d]", inode) for _, f := range files { @@ -177,12 +144,12 @@ func resolveProcessNameByProcSearch(inode, uid int32) (string, error) { if err != nil { return "", err } - if info.Sys().(*syscall.Stat_t).Uid != uint32(uid) { + if info.Sys().(*syscall.Stat_t).Uid != uid { continue } - processPath := path.Join(pathProc, f.Name()) - fdPath := path.Join(processPath, "fd") + processPath := filepath.Join("/proc", f.Name()) + fdPath := filepath.Join(processPath, "fd") fds, err := os.ReadDir(fdPath) if err != nil { @@ -190,13 +157,13 @@ func resolveProcessNameByProcSearch(inode, uid int32) (string, error) { } for _, fd := range fds { - n, err := syscall.Readlink(path.Join(fdPath, fd.Name()), buffer) + n, err := unix.Readlink(filepath.Join(fdPath, fd.Name()), buffer) if err != nil { continue } if bytes.Equal(buffer[:n], socket) { - return os.Readlink(path.Join(processPath, "exe")) + return os.Readlink(filepath.Join(processPath, "exe")) } } } diff --git a/go.mod b/go.mod index a6fe15e0..84f78766 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/gofrs/uuid v4.2.0+incompatible github.com/gorilla/websocket v1.5.0 github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84 + github.com/mdlayher/netlink v1.6.0 github.com/miekg/dns v1.1.50 github.com/oschwald/geoip2-golang v1.8.0 github.com/sirupsen/logrus v1.9.0 @@ -26,7 +27,10 @@ require ( require ( github.com/ajg/form v1.5.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/google/go-cmp v0.5.7 // indirect + github.com/josharian/native v1.0.0 // indirect github.com/kr/text v0.2.0 // indirect + github.com/mdlayher/socket v0.1.1 // indirect github.com/oschwald/maxminddb-golang v1.10.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect diff --git a/go.sum b/go.sum index a86cdaa7..4dc43b1f 100644 --- a/go.sum +++ b/go.sum @@ -18,12 +18,17 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84 h1:MJTy6H+EpXLeAn0P5WAWeLk6dJA3V0ik6S3VJfUyQuI= github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= +github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk= +github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= @@ -37,8 +42,12 @@ github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= +github.com/mdlayher/netlink v1.6.0 h1:rOHX5yl7qnlpiVkFWoqccueppMtXzeziFjWAjLg6sz0= +github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA= github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= +github.com/mdlayher/socket v0.1.1 h1:q3uOGirUPfAV2MUoaC7BavjQ154J7+JOkTWyiV+intI= +github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs= @@ -87,6 +96,8 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c h1:JVAXQ10yGGVbSyoer5VILysz6YKjdNT2bsvlayjqhes= golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -111,11 +122,16 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM= golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 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.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= From 827d5289bc07f4e05279e6724aa32bc196e4f249 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20G=C4=85sior?= <60886162+adriang-90@users.noreply.github.com> Date: Wed, 21 Sep 2022 15:09:11 +0200 Subject: [PATCH 056/250] Refactor: improve Dockerfile (#2246) --- Dockerfile | 20 ++++++++++++-------- Makefile | 3 --- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6e67d5d4..954ede77 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,18 +1,22 @@ -FROM golang:alpine as builder +FROM --platform=${BUILDPLATFORM} golang:alpine as builder -RUN apk add --no-cache make git && \ +RUN apk add --no-cache make git ca-certificates tzdata && \ wget -O /Country.mmdb https://github.com/Dreamacro/maxmind-geoip/releases/latest/download/Country.mmdb -WORKDIR /clash-src +WORKDIR /workdir COPY --from=tonistiigi/xx:golang / / -COPY . /clash-src -RUN go mod download && \ - make docker && \ - mv ./bin/clash-docker /clash +ARG TARGETOS TARGETARCH TARGETVARIANT + +RUN --mount=target=. \ + --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg/mod \ + make BINDIR= ${TARGETOS}-${TARGETARCH}${TARGETVARIANT} && \ + mv /clash* /clash FROM alpine:latest LABEL org.opencontainers.image.source="https://github.com/Dreamacro/clash" -RUN apk add --no-cache ca-certificates tzdata +COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ COPY --from=builder /Country.mmdb /root/.config/clash/ COPY --from=builder /clash / ENTRYPOINT ["/clash"] diff --git a/Makefile b/Makefile index 36d7b3a2..a9291a47 100644 --- a/Makefile +++ b/Makefile @@ -37,9 +37,6 @@ WINDOWS_ARCH_LIST = \ all: linux-amd64 darwin-amd64 windows-amd64 # Most used -docker: - $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ - darwin-amd64: GOARCH=amd64 GOOS=darwin $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ From d4113944820150b4ec7c672cf1e2ebaa79f70f9b Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Wed, 21 Sep 2022 21:17:59 +0800 Subject: [PATCH 057/250] Chore: rename linux-armv8 to linux-arm64, windows-arm32v7 to windows-armv7 --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index a9291a47..360a2cdf 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ PLATFORM_LIST = \ linux-armv5 \ linux-armv6 \ linux-armv7 \ - linux-armv8 \ + linux-arm64 \ linux-mips-softfloat \ linux-mips-hardfloat \ linux-mipsle-softfloat \ @@ -33,7 +33,7 @@ WINDOWS_ARCH_LIST = \ windows-amd64 \ windows-amd64-v3 \ windows-arm64 \ - windows-arm32v7 + windows-armv7 all: linux-amd64 darwin-amd64 windows-amd64 # Most used @@ -64,7 +64,7 @@ linux-armv6: linux-armv7: GOARCH=arm GOOS=linux GOARM=7 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ -linux-armv8: +linux-arm64: GOARCH=arm64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ linux-mips-softfloat: @@ -109,7 +109,7 @@ windows-amd64-v3: windows-arm64: GOARCH=arm64 GOOS=windows $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe -windows-arm32v7: +windows-armv7: GOARCH=arm GOOS=windows GOARM=7 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe gz_releases=$(addsuffix .gz, $(PLATFORM_LIST)) From fb85691fb90bc2584f52cfd402cc23d4f5ba87d7 Mon Sep 17 00:00:00 2001 From: AndyChen Date: Tue, 27 Sep 2022 14:22:21 +0800 Subject: [PATCH 058/250] Fix: uncorrect README link (#2325) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 73af1a65..6dff747b 100644 --- a/README.md +++ b/README.md @@ -36,9 +36,9 @@ Premium core is proprietary. You can find their release notes and pre-built binaries [here](https://github.com/Dreamacro/clash/releases/tag/premium). -- gvisor/system stack TUN device on macOS, Linux and Windows ([ref](https://github.com/Dreamacro/clash/wiki/premium-core-features#tun-device)) -- Policy routing with [Scripts](https://github.com/Dreamacro/clash/wiki/premium-core-features#script) -- Load your rules with [Rule Providers](https://github.com/Dreamacro/clash/wiki/premium-core-features#rule-providers) +- gvisor/system stack TUN device on macOS, Linux and Windows ([ref](https://github.com/Dreamacro/clash/wiki/Clash-Premium-Features#tun-device)) +- Policy routing with [Scripts](https://github.com/Dreamacro/clash/wiki/Clash-Premium-Features#script) +- Load your rules with [Rule Providers](https://github.com/Dreamacro/clash/wiki/Clash-Premium-Features#rule-providers) - Monitor Clash usage with a built-in profiling engine. ([Dreamacro/clash-tracing](https://github.com/Dreamacro/clash-tracing)) ## Getting Started From 3e2b08f9d0228ebccc09097f2dd0149c609890ef Mon Sep 17 00:00:00 2001 From: Pan <50233445+A7103@users.noreply.github.com> Date: Thu, 29 Sep 2022 11:47:30 +0800 Subject: [PATCH 059/250] Chore: upgrade go.mod go version to 1.19 (#2331) --- go.mod | 2 +- test/go.mod | 25 +++++++++++++----------- test/go.sum | 55 ++++++++++++++++++++++++++++++++++------------------- 3 files changed, 50 insertions(+), 32 deletions(-) diff --git a/go.mod b/go.mod index 84f78766..b4d5aeaa 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/Dreamacro/clash -go 1.18 +go 1.19 require ( github.com/go-chi/chi/v5 v5.0.7 diff --git a/test/go.mod b/test/go.mod index f61ed9d4..db218d21 100644 --- a/test/go.mod +++ b/test/go.mod @@ -1,6 +1,6 @@ module clash-test -go 1.18 +go 1.19 require ( github.com/Dreamacro/clash v1.11.4 @@ -8,7 +8,7 @@ require ( github.com/docker/go-connections v0.4.0 github.com/miekg/dns v1.1.50 github.com/stretchr/testify v1.8.0 - golang.org/x/net v0.0.0-20220708220712-1185a9018129 + golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c ) replace github.com/Dreamacro/clash => ../ @@ -20,25 +20,28 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/gofrs/uuid v4.2.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/google/go-cmp v0.5.6 // indirect + github.com/google/go-cmp v0.5.7 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f // indirect + github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84 // indirect + github.com/josharian/native v1.0.0 // indirect + github.com/mdlayher/netlink v1.6.0 // indirect + github.com/mdlayher/socket v0.1.1 // indirect github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect - github.com/oschwald/geoip2-golang v1.7.0 // indirect - github.com/oschwald/maxminddb-golang v1.9.0 // indirect + github.com/oschwald/geoip2-golang v1.8.0 // indirect + github.com/oschwald/maxminddb-golang v1.10.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/sirupsen/logrus v1.8.1 // indirect + github.com/sirupsen/logrus v1.9.0 // indirect github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect go.etcd.io/bbolt v1.3.6 // indirect - go.uber.org/atomic v1.9.0 // indirect - golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect + go.uber.org/atomic v1.10.0 // indirect + golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503 // indirect golang.org/x/mod v0.4.2 // indirect - golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect - golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e // indirect + golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect + golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect diff --git a/test/go.sum b/test/go.sum index be43b1f0..5a5557d0 100644 --- a/test/go.sum +++ b/test/go.sum @@ -25,14 +25,17 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= -github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f h1:l1QCwn715k8nYkj4Ql50rzEog3WnMdrd4YYMMwemxEo= -github.com/insomniacslk/dhcp v0.0.0-20220504074936-1ca156eafb9f/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= +github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84 h1:MJTy6H+EpXLeAn0P5WAWeLk6dJA3V0ik6S3VJfUyQuI= +github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= +github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk= +github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= @@ -50,8 +53,12 @@ github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= +github.com/mdlayher/netlink v1.6.0 h1:rOHX5yl7qnlpiVkFWoqccueppMtXzeziFjWAjLg6sz0= +github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA= github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= +github.com/mdlayher/socket v0.1.1 h1:q3uOGirUPfAV2MUoaC7BavjQ154J7+JOkTWyiV+intI= +github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= @@ -62,26 +69,26 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/oschwald/geoip2-golang v1.7.0 h1:JW1r5AKi+vv2ujSxjKthySK3jo8w8oKWPyXsw+Qs/S8= -github.com/oschwald/geoip2-golang v1.7.0/go.mod h1:mdI/C7iK7NVMcIDDtf4bCKMJ7r0o7UwGeCo9eiitCMQ= -github.com/oschwald/maxminddb-golang v1.9.0 h1:tIk4nv6VT9OiPyrnDAfJS1s1xKDQMZOsGojab6EjC1Y= -github.com/oschwald/maxminddb-golang v1.9.0/go.mod h1:TK+s/Z2oZq0rSl4PSeAEoP0bgm82Cp5HyvYbt8K3zLY= +github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs= +github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw= +github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg= +github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -92,13 +99,13 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= 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.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503 h1:vJ2V3lFLg+bBhgroYuRfyN583UzVveQmIXjc8T/y3to= +golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 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.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= @@ -117,14 +124,16 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220708220712-1185a9018129 h1:vucSRfWwTsoXro7P+3Cjlr6flUMtzCwzlvkxEQtHHB0= -golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c h1:JVAXQ10yGGVbSyoer5VILysz6YKjdNT2bsvlayjqhes= +golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 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.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc= +golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -146,11 +155,17 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e h1:CsOuNlbOuf0mzxJIefr6Q4uAUetRUwZE4qt7VfzP+xo= -golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM= +golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 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.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= From f29b54898f895e5df058116fbe9317bdf4caa151 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Thu, 27 Oct 2022 11:25:18 +0800 Subject: [PATCH 060/250] Fix: macOS Ventura process name match --- component/process/process_darwin.go | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/component/process/process_darwin.go b/component/process/process_darwin.go index df6f1be8..c98453eb 100644 --- a/component/process/process_darwin.go +++ b/component/process/process_darwin.go @@ -3,6 +3,8 @@ package process import ( "encoding/binary" "net" + "strconv" + "strings" "syscall" "unsafe" @@ -15,6 +17,22 @@ const ( proccallnumpidinfo = 0x2 ) +var structSize = func() int { + value, _ := syscall.Sysctl("kern.osproductversion") + major, _, _ := strings.Cut(value, ".") + n, _ := strconv.ParseInt(major, 10, 64) + switch true { + case n >= 13: + return 408 + default: + // from darwin-xnu/bsd/netinet/in_pcblist.c:get_pcblist_n + // size/offset are round up (aligned) to 8 bytes in darwin + // rup8(sizeof(xinpcb_n)) + rup8(sizeof(xsocket_n)) + + // 2 * rup8(sizeof(xsockbuf_n)) + rup8(sizeof(xsockstat_n)) + return 384 + } +}() + func findProcessName(network string, ip net.IP, port int) (string, error) { var spath string switch network { @@ -34,12 +52,7 @@ func findProcessName(network string, ip net.IP, port int) (string, error) { } buf := []byte(value) - - // from darwin-xnu/bsd/netinet/in_pcblist.c:get_pcblist_n - // size/offset are round up (aligned) to 8 bytes in darwin - // rup8(sizeof(xinpcb_n)) + rup8(sizeof(xsocket_n)) + - // 2 * rup8(sizeof(xsockbuf_n)) + rup8(sizeof(xsockstat_n)) - itemSize := 384 + itemSize := structSize if network == TCP { // rup8(sizeof(xtcpcb_n)) itemSize += 208 From c8bc11d61d578841b54e53e2a4a8731b3570cacd Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Thu, 27 Oct 2022 15:35:53 +0800 Subject: [PATCH 061/250] Fix: amd64 macOS Ventura process name match --- component/process/process_darwin.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/component/process/process_darwin.go b/component/process/process_darwin.go index c98453eb..814161c5 100644 --- a/component/process/process_darwin.go +++ b/component/process/process_darwin.go @@ -18,11 +18,11 @@ const ( ) var structSize = func() int { - value, _ := syscall.Sysctl("kern.osproductversion") + value, _ := syscall.Sysctl("kern.osrelease") major, _, _ := strings.Cut(value, ".") n, _ := strconv.ParseInt(major, 10, 64) switch true { - case n >= 13: + case n >= 22: return 408 default: // from darwin-xnu/bsd/netinet/in_pcblist.c:get_pcblist_n From c34c5ff1f922f3f736dfdcf3a77f22714b6fa4e1 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 2 Nov 2022 22:13:54 +0800 Subject: [PATCH 062/250] build: fix golang build cache --- .github/workflows/build.yaml | 12 +++++++----- .github/workflows/prerelease.yml | 21 +++++---------------- .github/workflows/release.yaml | 22 +++++++--------------- 3 files changed, 19 insertions(+), 36 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 87202e69..3e9d3d93 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -5,12 +5,14 @@ jobs: build: runs-on: ubuntu-latest steps: - - name: Set up Go - uses: actions/setup-go@v1 + - name: Check out code into the Go module directory + uses: actions/checkout@v3 + - name: Setup Go + uses: actions/setup-go@v3 with: - go-version: 1.18 - - name: Check out code - uses: actions/checkout@v1 + go-version: '1.19' + check-latest: true + cache: true - name: Build run: make all - name: Release diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index daaba026..afd49cff 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -12,26 +12,15 @@ jobs: Build: runs-on: ubuntu-latest steps: - - name: Get latest go version - id: version - run: | - echo ::set-output name=go_version::$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g') - - name: Setup Go - uses: actions/setup-go@v3 - with: - go-version: ${{ steps.version.outputs.go_version }} - - name: Check out code into the Go module directory uses: actions/checkout@v3 - - name: Cache go module - uses: actions/cache@v2 + - name: Setup Go + uses: actions/setup-go@v3 with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - + go-version: '1.19' + check-latest: true + cache: true - name: Test if: ${{github.ref_name=='Beta'}} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 4b2b60da..c74637a6 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -7,24 +7,16 @@ jobs: Build: runs-on: ubuntu-latest steps: - - name: Get latest go version - id: version - run: | - echo ::set-output name=go_version::$(curl -s https://raw.githubusercontent.com/actions/go-versions/main/versions-manifest.json | grep -oE '"version": "[0-9]{1}.[0-9]{1,}(.[0-9]{1,})?"' | head -1 | cut -d':' -f2 | sed 's/ //g; s/"//g') - - name: Setup Go - uses: actions/setup-go@v2 - with: - go-version: ${{ steps.version.outputs.go_version }} - - name: Check out code into the Go module directory uses: actions/checkout@v3 - - name: Cache go module - uses: actions/cache@v2 + + - name: Setup Go + uses: actions/setup-go@v3 with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- + go-version: '1.19' + check-latest: true + cache: true + - name: Test run: | go test ./... From 22fb219ad8c7b3535b8331abf8cb65a75716380f Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 2 Nov 2022 22:28:18 +0800 Subject: [PATCH 063/250] chore: trie.DomainTrie will not depend on zero value --- component/fakeip/pool.go | 4 ++-- component/resolver/resolver.go | 8 ++++---- component/sniffer/dispatcher.go | 8 ++++---- component/trie/domain.go | 16 ++++++++-------- component/trie/domain_test.go | 4 ++-- component/trie/node.go | 28 +++++++++++++++++++++++----- config/config.go | 24 ++++++++++++------------ dns/filters.go | 6 +++--- dns/middleware.go | 2 +- dns/resolver.go | 2 +- rules/provider/domain_strategy.go | 6 +++--- tunnel/tunnel.go | 6 +++--- 12 files changed, 66 insertions(+), 48 deletions(-) diff --git a/component/fakeip/pool.go b/component/fakeip/pool.go index 58a75a94..1ef50d95 100644 --- a/component/fakeip/pool.go +++ b/component/fakeip/pool.go @@ -34,7 +34,7 @@ type Pool struct { offset netip.Addr cycle bool mux sync.Mutex - host *trie.DomainTrie[bool] + host *trie.DomainTrie[struct{}] ipnet *netip.Prefix store store } @@ -150,7 +150,7 @@ func (p *Pool) restoreState() { type Options struct { IPNet *netip.Prefix - Host *trie.DomainTrie[bool] + Host *trie.DomainTrie[struct{}] // Size sets the maximum number of entries in memory // and does not work if Persistence is true diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index abb45564..af32cc94 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -127,7 +127,7 @@ func ResolveAllIPv6WithResolver(host string, r Resolver) ([]netip.Addr, error) { } if node := DefaultHosts.Search(host); node != nil { - if ip := node.Data; ip.Is6() { + if ip := node.Data(); ip.Is6() { return []netip.Addr{ip}, nil } } @@ -161,8 +161,8 @@ func ResolveAllIPv6WithResolver(host string, r Resolver) ([]netip.Addr, error) { func ResolveAllIPv4WithResolver(host string, r Resolver) ([]netip.Addr, error) { if node := DefaultHosts.Search(host); node != nil { - if ip := node.Data; ip.Is4() { - return []netip.Addr{node.Data}, nil + if ip := node.Data(); ip.Is4() { + return []netip.Addr{node.Data()}, nil } } @@ -200,7 +200,7 @@ func ResolveAllIPv4WithResolver(host string, r Resolver) ([]netip.Addr, error) { func ResolveAllIPWithResolver(host string, r Resolver) ([]netip.Addr, error) { if node := DefaultHosts.Search(host); node != nil { - return []netip.Addr{node.Data}, nil + return []netip.Addr{node.Data()}, nil } ip, err := netip.ParseAddr(host) diff --git a/component/sniffer/dispatcher.go b/component/sniffer/dispatcher.go index 6a5e632a..3bd81ac8 100644 --- a/component/sniffer/dispatcher.go +++ b/component/sniffer/dispatcher.go @@ -31,8 +31,8 @@ type SnifferDispatcher struct { sniffers []sniffer.Sniffer - forceDomain *trie.DomainTrie[bool] - skipSNI *trie.DomainTrie[bool] + forceDomain *trie.DomainTrie[struct{}] + skipSNI *trie.DomainTrie[struct{}] portRanges *[]utils.Range[uint16] skipList *cache.LruCache[string, uint8] rwMux sync.RWMutex @@ -183,8 +183,8 @@ func NewCloseSnifferDispatcher() (*SnifferDispatcher, error) { return &dispatcher, nil } -func NewSnifferDispatcher(needSniffer []sniffer.Type, forceDomain *trie.DomainTrie[bool], - skipSNI *trie.DomainTrie[bool], ports *[]utils.Range[uint16], +func NewSnifferDispatcher(needSniffer []sniffer.Type, forceDomain *trie.DomainTrie[struct{}], + skipSNI *trie.DomainTrie[struct{}], ports *[]utils.Range[uint16], forceDnsMapping bool, parsePureIp bool) (*SnifferDispatcher, error) { dispatcher := SnifferDispatcher{ enable: true, diff --git a/component/trie/domain.go b/component/trie/domain.go index 16dd9ae9..86c467db 100644 --- a/component/trie/domain.go +++ b/component/trie/domain.go @@ -17,7 +17,7 @@ var ErrInvalidDomain = errors.New("invalid domain") // DomainTrie contains the main logic for adding and searching nodes for domain segments. // support wildcard domain (e.g *.google.com) -type DomainTrie[T comparable] struct { +type DomainTrie[T any] struct { root *Node[T] } @@ -74,13 +74,13 @@ func (t *DomainTrie[T]) insert(parts []string, data T) { for i := len(parts) - 1; i >= 0; i-- { part := parts[i] if !node.hasChild(part) { - node.addChild(part, newNode(getZero[T]())) + node.addChild(part, newNode[T]()) } node = node.getChild(part) } - node.Data = data + node.setData(data) } // Search is the most important part of the Trie. @@ -96,7 +96,7 @@ func (t *DomainTrie[T]) Search(domain string) *Node[T] { n := t.search(t.root, parts) - if n == nil || n.Data == getZero[T]() { + if n.isEmpty() { return nil } @@ -109,13 +109,13 @@ func (t *DomainTrie[T]) search(node *Node[T], parts []string) *Node[T] { } if c := node.getChild(parts[len(parts)-1]); c != nil { - if n := t.search(c, parts[:len(parts)-1]); n != nil && n.Data != getZero[T]() { + if n := t.search(c, parts[:len(parts)-1]); !n.isEmpty() { return n } } if c := node.getChild(wildcard); c != nil { - if n := t.search(c, parts[:len(parts)-1]); n != nil && n.Data != getZero[T]() { + if n := t.search(c, parts[:len(parts)-1]); !n.isEmpty() { return n } } @@ -124,6 +124,6 @@ func (t *DomainTrie[T]) search(node *Node[T], parts []string) *Node[T] { } // New returns a new, empty Trie. -func New[T comparable]() *DomainTrie[T] { - return &DomainTrie[T]{root: newNode[T](getZero[T]())} +func New[T any]() *DomainTrie[T] { + return &DomainTrie[T]{root: newNode[T]()} } diff --git a/component/trie/domain_test.go b/component/trie/domain_test.go index ced44d03..c54b3d3b 100644 --- a/component/trie/domain_test.go +++ b/component/trie/domain_test.go @@ -23,7 +23,7 @@ func TestTrie_Basic(t *testing.T) { node := tree.Search("example.com") assert.NotNil(t, node) - assert.True(t, node.Data == localIP) + assert.True(t, node.Data() == localIP) assert.NotNil(t, tree.Insert("", localIP)) assert.Nil(t, tree.Search("")) assert.NotNil(t, tree.Search("localhost")) @@ -75,7 +75,7 @@ func TestTrie_Priority(t *testing.T) { assertFn := func(domain string, data int) { node := tree.Search(domain) assert.NotNil(t, node) - assert.Equal(t, data, node.Data) + assert.Equal(t, data, node.Data()) } for idx, domain := range domains { diff --git a/component/trie/node.go b/component/trie/node.go index 1545d880..9d45bda8 100644 --- a/component/trie/node.go +++ b/component/trie/node.go @@ -1,9 +1,10 @@ package trie // Node is the trie's node -type Node[T comparable] struct { +type Node[T any] struct { children map[string]*Node[T] - Data T + inited bool + data T } func (n *Node[T]) getChild(s string) *Node[T] { @@ -18,14 +19,31 @@ func (n *Node[T]) addChild(s string, child *Node[T]) { n.children[s] = child } -func newNode[T comparable](data T) *Node[T] { +func (n *Node[T]) isEmpty() bool { + if n == nil || n.inited == false { + return true + } + return false +} + +func (n *Node[T]) setData(data T) { + n.data = data + n.inited = true +} + +func (n *Node[T]) Data() T { + return n.data +} + +func newNode[T any]() *Node[T] { return &Node[T]{ - Data: data, children: map[string]*Node[T]{}, + inited: false, + data: getZero[T](), } } -func getZero[T comparable]() T { +func getZero[T any]() T { var result T return result } diff --git a/config/config.go b/config/config.go index ee4fbb16..9cad0915 100644 --- a/config/config.go +++ b/config/config.go @@ -197,9 +197,9 @@ type IPTables struct { type Sniffer struct { Enable bool Sniffers []sniffer.Type - Reverses *trie.DomainTrie[bool] - ForceDomain *trie.DomainTrie[bool] - SkipDomain *trie.DomainTrie[bool] + Reverses *trie.DomainTrie[struct{}] + ForceDomain *trie.DomainTrie[struct{}] + SkipDomain *trie.DomainTrie[struct{}] Ports *[]utils.Range[uint16] ForceDnsMapping bool ParsePureIp bool @@ -1061,24 +1061,24 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[netip.Addr], rules []C.R return nil, err } - var host *trie.DomainTrie[bool] + var host *trie.DomainTrie[struct{}] // fake ip skip host filter if len(cfg.FakeIPFilter) != 0 { - host = trie.New[bool]() + host = trie.New[struct{}]() for _, domain := range cfg.FakeIPFilter { - _ = host.Insert(domain, true) + _ = host.Insert(domain, struct{}{}) } } if len(dnsCfg.Fallback) != 0 { if host == nil { - host = trie.New[bool]() + host = trie.New[struct{}]() } for _, fb := range dnsCfg.Fallback { if net.ParseIP(fb.Addr) != nil { continue } - _ = host.Insert(fb.Addr, true) + _ = host.Insert(fb.Addr, struct{}{}) } } @@ -1232,17 +1232,17 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) { for st := range loadSniffer { sniffer.Sniffers = append(sniffer.Sniffers, st) } - sniffer.ForceDomain = trie.New[bool]() + sniffer.ForceDomain = trie.New[struct{}]() for _, domain := range snifferRaw.ForceDomain { - err := sniffer.ForceDomain.Insert(domain, true) + err := sniffer.ForceDomain.Insert(domain, struct{}{}) if err != nil { return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err) } } - sniffer.SkipDomain = trie.New[bool]() + sniffer.SkipDomain = trie.New[struct{}]() for _, domain := range snifferRaw.SkipDomain { - err := sniffer.SkipDomain.Insert(domain, true) + err := sniffer.SkipDomain.Insert(domain, struct{}{}) if err != nil { return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err) } diff --git a/dns/filters.go b/dns/filters.go index 80b656c9..5b0141a2 100644 --- a/dns/filters.go +++ b/dns/filters.go @@ -71,13 +71,13 @@ type fallbackDomainFilter interface { } type domainFilter struct { - tree *trie.DomainTrie[bool] + tree *trie.DomainTrie[struct{}] } func NewDomainFilter(domains []string) *domainFilter { - df := domainFilter{tree: trie.New[bool]()} + df := domainFilter{tree: trie.New[struct{}]()} for _, domain := range domains { - _ = df.tree.Insert(domain, true) + _ = df.tree.Insert(domain, struct{}{}) } return &df } diff --git a/dns/middleware.go b/dns/middleware.go index 0bfc4977..0e1335f9 100644 --- a/dns/middleware.go +++ b/dns/middleware.go @@ -37,7 +37,7 @@ func withHosts(hosts *trie.DomainTrie[netip.Addr], mapping *cache.LruCache[netip return next(ctx, r) } - ip := record.Data + ip := record.Data() msg := r.Copy() if ip.Is4() && q.Qtype == D.TypeA { diff --git a/dns/resolver.go b/dns/resolver.go index aac22cc8..84a38034 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -245,7 +245,7 @@ func (r *Resolver) matchPolicy(m *D.Msg) []dnsClient { return nil } - p := record.Data + p := record.Data() return p.GetData() } diff --git a/rules/provider/domain_strategy.go b/rules/provider/domain_strategy.go index a6145383..e8bebf6e 100644 --- a/rules/provider/domain_strategy.go +++ b/rules/provider/domain_strategy.go @@ -9,7 +9,7 @@ import ( type domainStrategy struct { count int - domainRules *trie.DomainTrie[bool] + domainRules *trie.DomainTrie[struct{}] } func (d *domainStrategy) Match(metadata *C.Metadata) bool { @@ -25,11 +25,11 @@ func (d *domainStrategy) ShouldResolveIP() bool { } func (d *domainStrategy) OnUpdate(rules []string) { - domainTrie := trie.New[bool]() + domainTrie := trie.New[struct{}]() count := 0 for _, rule := range rules { actualDomain, _ := idna.ToASCII(rule) - err := domainTrie.Insert(actualDomain, true) + err := domainTrie.Insert(actualDomain, struct{}{}) if err != nil { log.Warnln("invalid domain:[%s]", rule) } else { diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index ddf771b9..d9513e01 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -178,7 +178,7 @@ func preHandleMetadata(metadata *C.Metadata) error { metadata.DNSMode = C.DNSFakeIP } else if node := resolver.DefaultHosts.Search(host); node != nil { // redir-host should lookup the hosts - metadata.DstIP = node.Data + metadata.DstIP = node.Data() } } else if resolver.IsFakeIP(metadata.DstIP) { return fmt.Errorf("fake DNS record %s missing", metadata.DstIP) @@ -334,7 +334,7 @@ func handleTCPConn(connCtx C.ConnContext) { dialMetadata := metadata if len(metadata.Host) > 0 { if node := resolver.DefaultHosts.Search(metadata.Host); node != nil { - dialMetadata.DstIP = node.Data + dialMetadata.DstIP = node.Data() dialMetadata.DNSMode = C.DNSHosts dialMetadata = dialMetadata.Pure() } @@ -388,7 +388,7 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { ) if node := resolver.DefaultHosts.Search(metadata.Host); node != nil { - metadata.DstIP = node.Data + metadata.DstIP = node.Data() resolved = true } From 7b0cd14b004507fdc2aa04630f29f33af9ecc5cc Mon Sep 17 00:00:00 2001 From: adlyq Date: Wed, 2 Nov 2022 23:58:51 +0800 Subject: [PATCH 064/250] chore: netlink duplicate contains --- component/ebpf/ebpf_linux.go | 3 +-- component/ebpf/redir/auto_redirect.go | 2 +- component/ebpf/tc/redirect_to_tun.go | 2 +- go.mod | 5 +---- go.sum | 4 ---- listener/listener.go | 7 ++++--- listener/sing_tun/server.go | 7 ++++--- 7 files changed, 12 insertions(+), 18 deletions(-) diff --git a/component/ebpf/ebpf_linux.go b/component/ebpf/ebpf_linux.go index bf41d6cb..2ffd4bd5 100644 --- a/component/ebpf/ebpf_linux.go +++ b/component/ebpf/ebpf_linux.go @@ -6,13 +6,12 @@ import ( "fmt" "net/netip" - "github.com/vishvananda/netlink" - "github.com/Dreamacro/clash/common/cmd" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/ebpf/redir" "github.com/Dreamacro/clash/component/ebpf/tc" C "github.com/Dreamacro/clash/constant" + "github.com/sagernet/netlink" ) func GetAutoDetectInterface() (string, error) { diff --git a/component/ebpf/redir/auto_redirect.go b/component/ebpf/redir/auto_redirect.go index 2d1b4878..4fd8b785 100644 --- a/component/ebpf/redir/auto_redirect.go +++ b/component/ebpf/redir/auto_redirect.go @@ -13,7 +13,7 @@ import ( "github.com/cilium/ebpf" "github.com/cilium/ebpf/rlimit" - "github.com/vishvananda/netlink" + "github.com/sagernet/netlink" "golang.org/x/sys/unix" "github.com/Dreamacro/clash/component/ebpf/byteorder" diff --git a/component/ebpf/tc/redirect_to_tun.go b/component/ebpf/tc/redirect_to_tun.go index 3fbaeb1b..1edc1781 100644 --- a/component/ebpf/tc/redirect_to_tun.go +++ b/component/ebpf/tc/redirect_to_tun.go @@ -11,7 +11,7 @@ import ( "github.com/cilium/ebpf" "github.com/cilium/ebpf/rlimit" - "github.com/vishvananda/netlink" + "github.com/sagernet/netlink" "golang.org/x/sys/unix" C "github.com/Dreamacro/clash/constant" diff --git a/go.mod b/go.mod index 0c772909..580481f1 100644 --- a/go.mod +++ b/go.mod @@ -19,13 +19,13 @@ require ( github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/miekg/dns v1.1.50 github.com/oschwald/geoip2-golang v1.8.0 + github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 github.com/sagernet/sing v0.0.0-20220929000216-9a83e35b7186 github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 github.com/sagernet/sing-tun v0.0.0-20221012082254-488c3b75f6fd github.com/sagernet/sing-vmess v0.0.0-20220921140858-b6a1bdee672f github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.0 - github.com/vishvananda/netlink v1.2.1-beta.2 github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837 go.etcd.io/bbolt v1.3.6 go.uber.org/atomic v1.10.0 @@ -40,8 +40,6 @@ require ( ) -replace github.com/vishvananda/netlink => github.com/MetaCubeX/netlink v1.2.0-beta.0.20220529072258-d6853f887820 - replace github.com/lucas-clemente/quic-go => github.com/tobyxdd/quic-go v0.28.1-0.20220706211558-7780039ad599 require ( @@ -63,7 +61,6 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e // 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/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 // indirect github.com/vishvananda/netns v0.0.0-20220913150850-18c4f4234207 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect diff --git a/go.sum b/go.sum index d45870ef..6c5279d7 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,6 @@ dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1 dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/MetaCubeX/netlink v1.2.0-beta.0.20220529072258-d6853f887820 h1:fGKWZ25VApYnuPZoNeqdH/nZtHa2XMajwH6Yj/OgoVc= -github.com/MetaCubeX/netlink v1.2.0-beta.0.20220529072258-d6853f887820/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= @@ -239,7 +237,6 @@ github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 h1:hl6sK6aFgTLISijk6xIz github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= -github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/vishvananda/netns v0.0.0-20220913150850-18c4f4234207 h1:nn7SOQy8xCu3iXNv7oiBhhEQtbWdnEOMnuKBlHvrqIM= github.com/vishvananda/netns v0.0.0-20220913150850-18c4f4234207/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837 h1:AHhUwwFJGl27E46OpdJHplZkK09m7aETNBNzhT6t15M= @@ -336,7 +333,6 @@ golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/listener/listener.go b/listener/listener.go index 8512c0b1..d13093e3 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -337,7 +337,10 @@ func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P func ReCreateTun(tunConf *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { tunMux.Lock() - defer tunMux.Unlock() + defer func() { + lastTunConf = tunConf + tunMux.Unlock() + }() var err error defer func() { @@ -361,8 +364,6 @@ func ReCreateTun(tunConf *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- * } tunLister, err = sing_tun.New(*tunConf, tcpIn, udpIn) - - lastTunConf = tunConf } func ReCreateRedirToTun(ifaceNames []string) { diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 44824b75..f1327b33 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -204,10 +204,11 @@ func New(options config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P return } err = l.tunStack.Start() + if err != nil { return } - log.Infoln("Tun adapter listening at: %s(%s,%s), mtu: %d, auto route: %v, ip stack: %s", + log.Infoln("[TUN] Tun adapter listening at: %s(%s,%s), mtu: %d, auto route: %v, ip stack: %s", tunName, tunOptions.Inet4Address, tunOptions.Inet6Address, tunMTU, options.AutoRoute, options.Stack) return } @@ -218,9 +219,9 @@ func (l *Listener) FlushDefaultInterface() { for _, destination := range []netip.Addr{netip.IPv4Unspecified(), netip.IPv6Unspecified(), netip.MustParseAddr("1.1.1.1")} { autoDetectInterfaceName := l.defaultInterfaceMonitor.DefaultInterfaceName(destination) if autoDetectInterfaceName == l.tunName { - log.Warnln("Auto detect interface by %s get same name with tun", destination.String()) + log.Warnln("[TUN] Auto detect interface by %s get same name with tun", destination.String()) } else if autoDetectInterfaceName == "" || autoDetectInterfaceName == "" { - log.Warnln("Auto detect interface by %s get empty name.", destination.String()) + log.Warnln("[TUN] Auto detect interface by %s get empty name.", destination.String()) } else { targetInterface = autoDetectInterfaceName if old := dialer.DefaultInterface.Load(); old != targetInterface { From 508e257543b30cfb25cac3b0be2fb59eaeed71b8 Mon Sep 17 00:00:00 2001 From: adlyq Date: Thu, 3 Nov 2022 00:31:31 +0800 Subject: [PATCH 065/250] fix: RESTful API sniffingEnable --- tunnel/tunnel.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index d9513e01..760fa103 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -31,7 +31,7 @@ var ( proxies = make(map[string]C.Proxy) providers map[string]provider.ProxyProvider ruleProviders map[string]provider.RuleProvider - sniffingEnable bool + sniffingEnable = false configMux sync.RWMutex // Outbound Rule @@ -108,7 +108,7 @@ func UpdateProxies(newProxies map[string]C.Proxy, newProviders map[string]provid func UpdateSniffer(dispatcher *sniffer.SnifferDispatcher) { configMux.Lock() sniffer.Dispatcher = dispatcher - sniffingEnable = true + sniffingEnable = dispatcher.Enable() configMux.Unlock() } From 3373b62b02282982d4646548e36383ea45d33615 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 3 Nov 2022 12:58:21 +0800 Subject: [PATCH 066/250] fix: try to support android hotspot when using tun --- listener/sing_tun/server.go | 5 ++++- listener/sing_tun/server_android.go | 26 ++++++++++++++++++++++++++ listener/sing_tun/server_notandroid.go | 1 + 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index f1327b33..83d38e28 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -203,11 +203,14 @@ func New(options config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P if err != nil { return } - err = l.tunStack.Start() + err = l.tunStack.Start() if err != nil { return } + + l.openAndroidHotspot(tunOptions) + log.Infoln("[TUN] Tun adapter listening at: %s(%s,%s), mtu: %d, auto route: %v, ip stack: %s", tunName, tunOptions.Inet4Address, tunOptions.Inet6Address, tunMTU, options.AutoRoute, options.Stack) return diff --git a/listener/sing_tun/server_android.go b/listener/sing_tun/server_android.go index e9d36f66..f8befe6e 100644 --- a/listener/sing_tun/server_android.go +++ b/listener/sing_tun/server_android.go @@ -1,7 +1,11 @@ package sing_tun import ( + "github.com/Dreamacro/clash/log" + "github.com/sagernet/netlink" tun "github.com/sagernet/sing-tun" + "golang.org/x/sys/unix" + "runtime" ) func (l *Listener) buildAndroidRules(tunOptions *tun.Options) error { @@ -21,3 +25,25 @@ func (l *Listener) buildAndroidRules(tunOptions *tun.Options) error { func (h *ListenerHandler) OnPackagesUpdated(packages int, sharedUsers int) { return } + +func (l *Listener) openAndroidHotspot(tunOptions tun.Options) { + if runtime.GOOS == "android" && tunOptions.AutoRoute { + priority := 9000 + if len(tunOptions.ExcludedRanges()) > 0 { + priority++ + } + if tunOptions.InterfaceMonitor.AndroidVPNEnabled() { + priority++ + } + it := netlink.NewRule() + it.Priority = priority + it.IifName = tunOptions.Name + it.Table = 254 //main + it.Family = unix.AF_INET + it.SuppressPrefixlen = 0 + err := netlink.RuleAdd(it) + if err != nil { + log.Warnln("[TUN] add AndroidHotspot rule error") + } + } +} diff --git a/listener/sing_tun/server_notandroid.go b/listener/sing_tun/server_notandroid.go index f621c71e..6db98989 100644 --- a/listener/sing_tun/server_notandroid.go +++ b/listener/sing_tun/server_notandroid.go @@ -9,3 +9,4 @@ import ( func (l *Listener) buildAndroidRules(tunOptions *tun.Options) error { return nil } +func (l *Listener) openAndroidHotspot(tunOptions tun.Options) {} From 1b0d09068b746308de3b854e487c12c4ceac0871 Mon Sep 17 00:00:00 2001 From: adlyq Date: Thu, 3 Nov 2022 18:04:22 +0800 Subject: [PATCH 067/250] fix: RESTful API empty tun device name --- listener/listener.go | 2 +- listener/sing_tun/server.go | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/listener/listener.go b/listener/listener.go index d13093e3..2d95ed95 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -363,7 +363,7 @@ func ReCreateTun(tunConf *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- * return } - tunLister, err = sing_tun.New(*tunConf, tcpIn, udpIn) + tunLister, err = sing_tun.New(tunConf, tcpIn, udpIn) } func ReCreateRedirToTun(ifaceNames []string) { diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 83d38e28..23774683 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -65,10 +65,11 @@ func CalculateInterfaceName(name string) (tunName string) { return } -func New(options config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (l *Listener, err error) { +func New(options *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (l *Listener, err error) { tunName := options.Device if tunName == "" { tunName = CalculateInterfaceName(InterfaceName) + options.Device = tunName } tunMTU := options.MTU if tunMTU == 0 { @@ -121,7 +122,7 @@ func New(options config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P } l = &Listener{ closed: false, - options: options, + options: *options, handler: handler, } defer func() { From 9a5c0a4b6d99a299159c2c300e5892e17ad0c04b Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 3 Nov 2022 18:56:03 +0800 Subject: [PATCH 068/250] chore: better tun config passing --- hub/executor/executor.go | 5 ++++- listener/listener.go | 33 ++++++++++++++++----------------- listener/sing_tun/server.go | 4 ++-- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/hub/executor/executor.go b/hub/executor/executor.go index e5bb1ad7..1a785aed 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -259,7 +259,10 @@ func loadProxyProvider(proxyProviders map[string]provider.ProxyProvider) { } func updateTun(tun *config.Tun) { - P.ReCreateTun(tun, tunnel.TCPIn(), tunnel.UDPIn()) + if tun == nil { + return + } + P.ReCreateTun(*tun, tunnel.TCPIn(), tunnel.UDPIn()) P.ReCreateRedirToTun(tun.RedirectToTun) } diff --git a/listener/listener.go b/listener/listener.go index 2d95ed95..4983751b 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -26,7 +26,6 @@ import ( var ( allowLan = false bindAddress = "*" - lastTunConf *config.Tun inboundTfo = false socksListener *socks.Listener @@ -52,6 +51,8 @@ var ( tunMux sync.Mutex autoRedirMux sync.Mutex tcMux sync.Mutex + + lastTunConf config.Tun ) type Ports struct { @@ -63,12 +64,12 @@ type Ports struct { } func GetTunConf() config.Tun { - if lastTunConf == nil { + if tunLister == nil { return config.Tun{ Enable: false, } } - return *lastTunConf + return tunLister.Config() } func AllowLan() bool { @@ -335,7 +336,7 @@ func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P log.Infoln("Mixed(http+socks) proxy listening at: %s", mixedListener.Address()) } -func ReCreateTun(tunConf *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { +func ReCreateTun(tunConf config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { tunMux.Lock() defer func() { lastTunConf = tunConf @@ -383,11 +384,13 @@ func ReCreateRedirToTun(ifaceNames []string) { return } - if lastTunConf == nil || !lastTunConf.Enable { + tunConf := GetTunConf() + + if !tunConf.Enable { return } - program, err := ebpf.NewTcEBpfProgram(nicArr, lastTunConf.Device) + program, err := ebpf.NewTcEBpfProgram(nicArr, tunConf.Device) if err != nil { log.Errorln("Attached tc ebpf program error: %v", err) return @@ -509,8 +512,12 @@ func genAddr(host string, port int, allowLan bool) string { return fmt.Sprintf("127.0.0.1:%d", port) } -func hasTunConfigChange(tunConf *config.Tun) bool { - if lastTunConf == nil { +func hasTunConfigChange(tunConf config.Tun) bool { + if lastTunConf.Enable != tunConf.Enable || + lastTunConf.Device != tunConf.Device || + lastTunConf.Stack != tunConf.Stack || + lastTunConf.AutoRoute != tunConf.AutoRoute || + lastTunConf.AutoDetectInterface != tunConf.AutoDetectInterface { return true } @@ -532,14 +539,6 @@ func hasTunConfigChange(tunConf *config.Tun) bool { } } - if lastTunConf.Enable != tunConf.Enable || - lastTunConf.Device != tunConf.Device || - lastTunConf.Stack != tunConf.Stack || - lastTunConf.AutoRoute != tunConf.AutoRoute || - lastTunConf.AutoDetectInterface != tunConf.AutoDetectInterface { - return true - } - if slices.Equal(tunConf.Inet4Address, lastTunConf.Inet4Address) && slices.Equal(tunConf.Inet6Address, lastTunConf.Inet6Address) { return true } @@ -552,5 +551,5 @@ func Cleanup(wait bool) { tunLister.Close() tunLister = nil } - lastTunConf = nil + lastTunConf = config.Tun{} } diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 23774683..42e31c09 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -65,7 +65,7 @@ func CalculateInterfaceName(name string) (tunName string) { return } -func New(options *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (l *Listener, err error) { +func New(options config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (l *Listener, err error) { tunName := options.Device if tunName == "" { tunName = CalculateInterfaceName(InterfaceName) @@ -122,7 +122,7 @@ func New(options *config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound. } l = &Listener{ closed: false, - options: *options, + options: options, handler: handler, } defer func() { From e20d01a679b4bbef2befa069d68f8dd781f88477 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 4 Nov 2022 08:52:30 +0800 Subject: [PATCH 069/250] chore: try to let tun's restful patch api work --- config/config.go | 14 +++---- hub/executor/executor.go | 12 +++--- hub/route/configs.go | 91 +++++++++++++++++++++++++++++++++++++++- listener/listener.go | 91 ++++++++++++++++++++++++++++++---------- 4 files changed, 172 insertions(+), 36 deletions(-) diff --git a/config/config.go b/config/config.go index 9cad0915..e0efcf7d 100644 --- a/config/config.go +++ b/config/config.go @@ -213,7 +213,6 @@ type Experimental struct { // Config is clash config manager type Config struct { General *General - Tun *Tun IPTables *IPTables DNS *DNS Experimental *Experimental @@ -496,11 +495,10 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } config.DNS = dnsCfg - tunCfg, err := parseTun(rawCfg.Tun, config.General, dnsCfg) + err = parseTun(rawCfg.Tun, config.General, dnsCfg) if err != nil { return nil, err } - config.Tun = tunCfg config.Users = parseAuthentication(rawCfg.Authentication) @@ -1126,7 +1124,7 @@ func parseAuthentication(rawRecords []string) []auth.AuthUser { return users } -func parseTun(rawTun RawTun, general *General, dnsCfg *DNS) (*Tun, error) { +func parseTun(rawTun RawTun, general *General, dnsCfg *DNS) error { var dnsHijack []netip.AddrPort for _, d := range rawTun.DNSHijack { @@ -1136,7 +1134,7 @@ func parseTun(rawTun RawTun, general *General, dnsCfg *DNS) (*Tun, error) { d = strings.Replace(d, "any", "0.0.0.0", 1) addrPort, err := netip.ParseAddrPort(d) if err != nil { - return nil, fmt.Errorf("parse dns-hijack url error: %w", err) + return fmt.Errorf("parse dns-hijack url error: %w", err) } dnsHijack = append(dnsHijack, addrPort) @@ -1150,7 +1148,7 @@ func parseTun(rawTun RawTun, general *General, dnsCfg *DNS) (*Tun, error) { } tunAddressPrefix = netip.PrefixFrom(tunAddressPrefix.Addr(), 30) - return &Tun{ + general.Tun = Tun{ Enable: rawTun.Enable, Device: rawTun.Device, Stack: rawTun.Stack, @@ -1174,7 +1172,9 @@ func parseTun(rawTun RawTun, general *General, dnsCfg *DNS) (*Tun, error) { ExcludePackage: rawTun.ExcludePackage, EndpointIndependentNat: rawTun.EndpointIndependentNat, UDPTimeout: rawTun.UDPTimeout, - }, nil + } + + return nil } func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) { diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 1a785aed..760cb52f 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -86,7 +86,7 @@ func ApplyConfig(cfg *config.Config, force bool) { loadRuleProvider(cfg.RuleProviders) updateGeneral(cfg.General, force) updateIPTables(cfg) - updateTun(cfg.Tun) + updateTun(cfg.General) updateExperimental(cfg) log.SetLevel(cfg.General.LogLevel) @@ -258,12 +258,12 @@ func loadProxyProvider(proxyProviders map[string]provider.ProxyProvider) { wg.Wait() } -func updateTun(tun *config.Tun) { - if tun == nil { +func updateTun(general *config.General) { + if general == nil { return } - P.ReCreateTun(*tun, tunnel.TCPIn(), tunnel.UDPIn()) - P.ReCreateRedirToTun(tun.RedirectToTun) + P.ReCreateTun(general.Tun, tunnel.TCPIn(), tunnel.UDPIn()) + P.ReCreateRedirToTun(general.Tun.RedirectToTun) } func updateSniffer(sniffer *config.Sniffer) { @@ -403,7 +403,7 @@ func updateIPTables(cfg *config.Config) { } }() - if cfg.Tun.Enable { + if cfg.General.Tun.Enable { err = fmt.Errorf("when tun is enabled, iptables cannot be set automatically") return } diff --git a/hub/route/configs.go b/hub/route/configs.go index 82f65284..773b7372 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -1,14 +1,16 @@ package route import ( - "github.com/Dreamacro/clash/component/dialer" "net/http" + "net/netip" "path/filepath" "sync" + "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/resolver" "github.com/Dreamacro/clash/config" "github.com/Dreamacro/clash/constant" + C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/hub/executor" P "github.com/Dreamacro/clash/listener" "github.com/Dreamacro/clash/log" @@ -38,7 +40,7 @@ type configSchema struct { RedirPort *int `json:"redir-port"` TProxyPort *int `json:"tproxy-port"` MixedPort *int `json:"mixed-port"` - Tun *config.Tun `json:"tun"` + Tun *tunSchema `json:"tun"` AllowLan *bool `json:"allow-lan"` BindAddress *string `json:"bind-address"` Mode *tunnel.TunnelMode `json:"mode"` @@ -49,6 +51,32 @@ type configSchema struct { InterfaceName *string `json:"interface-name"` } +type tunSchema struct { + Enable bool `yaml:"enable" json:"enable"` + Device *string `yaml:"device" json:"device"` + Stack *C.TUNStack `yaml:"stack" json:"stack"` + DNSHijack *[]netip.AddrPort `yaml:"dns-hijack" json:"dns-hijack"` + AutoRoute *bool `yaml:"auto-route" json:"auto-route"` + AutoDetectInterface *bool `yaml:"auto-detect-interface" json:"auto-detect-interface"` + //RedirectToTun []string `yaml:"-" json:"-"` + + MTU *uint32 `yaml:"mtu" json:"mtu,omitempty"` + //Inet4Address *[]config.ListenPrefix `yaml:"inet4-address" json:"inet4_address,omitempty"` + Inet6Address *[]config.ListenPrefix `yaml:"inet6-address" json:"inet6_address,omitempty"` + StrictRoute *bool `yaml:"strict-route" json:"strict_route,omitempty"` + Inet4RouteAddress *[]config.ListenPrefix `yaml:"inet4_route_address" json:"inet4_route_address,omitempty"` + Inet6RouteAddress *[]config.ListenPrefix `yaml:"inet6_route_address" json:"inet6_route_address,omitempty"` + IncludeUID *[]uint32 `yaml:"include-uid" json:"include_uid,omitempty"` + IncludeUIDRange *[]string `yaml:"include-uid-range" json:"include_uid_range,omitempty"` + ExcludeUID *[]uint32 `yaml:"exclude-uid" json:"exclude_uid,omitempty"` + ExcludeUIDRange *[]string `yaml:"exclude-uid-range" json:"exclude_uid_range,omitempty"` + IncludeAndroidUser *[]int `yaml:"include-android-user" json:"include_android_user,omitempty"` + IncludePackage *[]string `yaml:"include-package" json:"include_package,omitempty"` + ExcludePackage *[]string `yaml:"exclude-package" json:"exclude_package,omitempty"` + EndpointIndependentNat *bool `yaml:"endpoint-independent-nat" json:"endpoint_independent_nat,omitempty"` + UDPTimeout *int64 `yaml:"udp-timeout" json:"udp_timeout,omitempty"` +} + func getConfigs(w http.ResponseWriter, r *http.Request) { general := executor.GetGeneral() render.JSON(w, r, general) @@ -62,6 +90,64 @@ func pointerOrDefault(p *int, def int) int { return def } +func pointerOrDefaultTun(p *tunSchema, def config.Tun) config.Tun { + if p != nil { + def.Enable = p.Enable + if p.Device != nil { + def.Device = *p.Device + } + if p.Stack != nil { + def.Stack = *p.Stack + } + if p.DNSHijack != nil { + def.DNSHijack = *p.DNSHijack + } + if p.AutoRoute != nil { + def.AutoRoute = *p.AutoRoute + } + if p.AutoDetectInterface != nil { + def.AutoDetectInterface = *p.AutoDetectInterface + } + if p.MTU != nil { + def.MTU = *p.MTU + } + //if p.Inet4Address != nil { + // def.Inet4Address = *p.Inet4Address + //} + if p.Inet6Address != nil { + def.Inet6Address = *p.Inet6Address + } + if p.IncludeUID != nil { + def.IncludeUID = *p.IncludeUID + } + if p.IncludeUIDRange != nil { + def.IncludeUIDRange = *p.IncludeUIDRange + } + if p.ExcludeUID != nil { + def.ExcludeUID = *p.ExcludeUID + } + if p.ExcludeUIDRange != nil { + def.ExcludeUIDRange = *p.ExcludeUIDRange + } + if p.IncludeAndroidUser != nil { + def.IncludeAndroidUser = *p.IncludeAndroidUser + } + if p.IncludePackage != nil { + def.IncludePackage = *p.IncludePackage + } + if p.ExcludePackage != nil { + def.ExcludePackage = *p.ExcludePackage + } + if p.EndpointIndependentNat != nil { + def.EndpointIndependentNat = *p.EndpointIndependentNat + } + if p.UDPTimeout != nil { + def.UDPTimeout = *p.UDPTimeout + } + } + return def +} + func patchConfigs(w http.ResponseWriter, r *http.Request) { general := &configSchema{} if err := render.DecodeJSON(r.Body, general); err != nil { @@ -100,6 +186,7 @@ func patchConfigs(w http.ResponseWriter, r *http.Request) { P.ReCreateRedir(pointerOrDefault(general.RedirPort, ports.RedirPort), tcpIn, udpIn) P.ReCreateTProxy(pointerOrDefault(general.TProxyPort, ports.TProxyPort), tcpIn, udpIn) P.ReCreateMixed(pointerOrDefault(general.MixedPort, ports.MixedPort), tcpIn, udpIn) + P.ReCreateTun(pointerOrDefaultTun(general.Tun, P.LastTunConf), tcpIn, udpIn) if general.Mode != nil { tunnel.SetMode(*general.Mode) diff --git a/listener/listener.go b/listener/listener.go index 4983751b..e608cb1e 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -52,7 +52,7 @@ var ( autoRedirMux sync.Mutex tcMux sync.Mutex - lastTunConf config.Tun + LastTunConf config.Tun ) type Ports struct { @@ -339,7 +339,7 @@ func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P func ReCreateTun(tunConf config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { tunMux.Lock() defer func() { - lastTunConf = tunConf + LastTunConf = tunConf tunMux.Unlock() }() @@ -351,7 +351,7 @@ func ReCreateTun(tunConf config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *i } }() - if !hasTunConfigChange(tunConf) { + if !hasTunConfigChange(&tunConf) { if tunLister != nil { tunLister.FlushDefaultInterface() } @@ -512,34 +512,83 @@ func genAddr(host string, port int, allowLan bool) string { return fmt.Sprintf("127.0.0.1:%d", port) } -func hasTunConfigChange(tunConf config.Tun) bool { - if lastTunConf.Enable != tunConf.Enable || - lastTunConf.Device != tunConf.Device || - lastTunConf.Stack != tunConf.Stack || - lastTunConf.AutoRoute != tunConf.AutoRoute || - lastTunConf.AutoDetectInterface != tunConf.AutoDetectInterface { +func hasTunConfigChange(tunConf *config.Tun) bool { + if LastTunConf.Enable != tunConf.Enable || + LastTunConf.Device != tunConf.Device || + LastTunConf.Stack != tunConf.Stack || + LastTunConf.AutoRoute != tunConf.AutoRoute || + LastTunConf.AutoDetectInterface != tunConf.AutoDetectInterface || + LastTunConf.MTU != tunConf.MTU || + LastTunConf.StrictRoute != tunConf.StrictRoute || + LastTunConf.EndpointIndependentNat != tunConf.EndpointIndependentNat || + LastTunConf.UDPTimeout != tunConf.UDPTimeout { return true } - if len(lastTunConf.DNSHijack) != len(tunConf.DNSHijack) { + if len(LastTunConf.DNSHijack) != len(tunConf.DNSHijack) { return true } - sort.Slice(lastTunConf.DNSHijack, func(i, j int) bool { - return lastTunConf.DNSHijack[i].Addr().Less(lastTunConf.DNSHijack[j].Addr()) - }) - sort.Slice(tunConf.DNSHijack, func(i, j int) bool { return tunConf.DNSHijack[i].Addr().Less(tunConf.DNSHijack[j].Addr()) }) - for i, dns := range tunConf.DNSHijack { - if dns != lastTunConf.DNSHijack[i] { - return true - } - } + sort.Slice(tunConf.Inet4Address, func(i, j int) bool { + return tunConf.Inet4Address[i].Build().String() < tunConf.Inet4Address[j].Build().String() + }) - if slices.Equal(tunConf.Inet4Address, lastTunConf.Inet4Address) && slices.Equal(tunConf.Inet6Address, lastTunConf.Inet6Address) { + sort.Slice(tunConf.Inet6Address, func(i, j int) bool { + return tunConf.Inet6Address[i].Build().String() < tunConf.Inet6Address[j].Build().String() + }) + + sort.Slice(tunConf.Inet4RouteAddress, func(i, j int) bool { + return tunConf.Inet4RouteAddress[i].Build().String() < tunConf.Inet4RouteAddress[j].Build().String() + }) + + sort.Slice(tunConf.Inet6RouteAddress, func(i, j int) bool { + return tunConf.Inet6RouteAddress[i].Build().String() < tunConf.Inet6RouteAddress[j].Build().String() + }) + + sort.Slice(tunConf.IncludeUID, func(i, j int) bool { + return tunConf.IncludeUID[i] < tunConf.IncludeUID[j] + }) + + sort.Slice(tunConf.IncludeUIDRange, func(i, j int) bool { + return tunConf.IncludeUIDRange[i] < tunConf.IncludeUIDRange[j] + }) + + sort.Slice(tunConf.ExcludeUID, func(i, j int) bool { + return tunConf.ExcludeUID[i] < tunConf.ExcludeUID[j] + }) + + sort.Slice(tunConf.ExcludeUIDRange, func(i, j int) bool { + return tunConf.ExcludeUIDRange[i] < tunConf.ExcludeUIDRange[j] + }) + + sort.Slice(tunConf.IncludeAndroidUser, func(i, j int) bool { + return tunConf.IncludeAndroidUser[i] < tunConf.IncludeAndroidUser[j] + }) + + sort.Slice(tunConf.IncludePackage, func(i, j int) bool { + return tunConf.IncludePackage[i] < tunConf.IncludePackage[j] + }) + + sort.Slice(tunConf.ExcludePackage, func(i, j int) bool { + return tunConf.ExcludePackage[i] < tunConf.ExcludePackage[j] + }) + + if !slices.Equal(tunConf.DNSHijack, LastTunConf.DNSHijack) || + !slices.Equal(tunConf.Inet4Address, LastTunConf.Inet4Address) || + !slices.Equal(tunConf.Inet6Address, LastTunConf.Inet6Address) || + !slices.Equal(tunConf.Inet4RouteAddress, LastTunConf.Inet4RouteAddress) || + !slices.Equal(tunConf.Inet6RouteAddress, LastTunConf.Inet6RouteAddress) || + !slices.Equal(tunConf.IncludeUID, LastTunConf.IncludeUID) || + !slices.Equal(tunConf.IncludeUIDRange, LastTunConf.IncludeUIDRange) || + !slices.Equal(tunConf.ExcludeUID, LastTunConf.ExcludeUID) || + !slices.Equal(tunConf.ExcludeUIDRange, LastTunConf.ExcludeUIDRange) || + !slices.Equal(tunConf.IncludeAndroidUser, LastTunConf.IncludeAndroidUser) || + !slices.Equal(tunConf.IncludePackage, LastTunConf.IncludePackage) || + !slices.Equal(tunConf.ExcludePackage, LastTunConf.ExcludePackage) { return true } @@ -551,5 +600,5 @@ func Cleanup(wait bool) { tunLister.Close() tunLister = nil } - lastTunConf = config.Tun{} + LastTunConf = config.Tun{} } From 19b7c7f52afc4d43fa87a8dc9bbdb7ca340c2646 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 4 Nov 2022 13:11:01 +0800 Subject: [PATCH 070/250] Fix: a shared fastSingle.Do() may cause providers untouched (#2378) --- adapter/outboundgroup/common.go | 11 ++++++++--- adapter/outboundgroup/urltest.go | 5 ++++- adapter/provider/provider.go | 6 ++---- constant/provider/interface.go | 4 ++-- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/adapter/outboundgroup/common.go b/adapter/outboundgroup/common.go index 758c7801..b1f05708 100644 --- a/adapter/outboundgroup/common.go +++ b/adapter/outboundgroup/common.go @@ -11,14 +11,19 @@ const ( defaultGetProxiesDuration = time.Second * 5 ) +func touchProviders(providers []provider.ProxyProvider) { + for _, provider := range providers { + provider.Touch() + } +} + func getProvidersProxies(providers []provider.ProxyProvider, touch bool) []C.Proxy { proxies := []C.Proxy{} for _, provider := range providers { if touch { - proxies = append(proxies, provider.ProxiesWithTouch()...) - } else { - proxies = append(proxies, provider.Proxies()...) + provider.Touch() } + proxies = append(proxies, provider.Proxies()...) } return proxies } diff --git a/adapter/outboundgroup/urltest.go b/adapter/outboundgroup/urltest.go index fd6d6818..79234ecb 100644 --- a/adapter/outboundgroup/urltest.go +++ b/adapter/outboundgroup/urltest.go @@ -66,7 +66,7 @@ func (u *URLTest) proxies(touch bool) []C.Proxy { } func (u *URLTest) fast(touch bool) C.Proxy { - elm, _, _ := u.fastSingle.Do(func() (any, error) { + elm, _, shared := u.fastSingle.Do(func() (any, error) { proxies := u.proxies(touch) fast := proxies[0] min := fast.LastDelay() @@ -95,6 +95,9 @@ func (u *URLTest) fast(touch bool) C.Proxy { return u.fastNode, nil }) + if shared && touch { // a shared fastSingle.Do() may cause providers untouched, so we touch them again + touchProviders(u.providers) + } return elm.(C.Proxy) } diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index 442b4b44..80611ace 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -78,9 +78,8 @@ func (pp *proxySetProvider) Proxies() []C.Proxy { return pp.proxies } -func (pp *proxySetProvider) ProxiesWithTouch() []C.Proxy { +func (pp *proxySetProvider) Touch() { pp.healthCheck.touch() - return pp.Proxies() } func (pp *proxySetProvider) setProxies(proxies []C.Proxy) { @@ -205,9 +204,8 @@ func (cp *compatibleProvider) Proxies() []C.Proxy { return cp.proxies } -func (cp *compatibleProvider) ProxiesWithTouch() []C.Proxy { +func (cp *compatibleProvider) Touch() { cp.healthCheck.touch() - return cp.Proxies() } func stopCompatibleProvider(pd *CompatibleProvider) { diff --git a/constant/provider/interface.go b/constant/provider/interface.go index 53bda7ea..1864f36d 100644 --- a/constant/provider/interface.go +++ b/constant/provider/interface.go @@ -66,9 +66,9 @@ type Provider interface { type ProxyProvider interface { Provider Proxies() []constant.Proxy - // ProxiesWithTouch is used to inform the provider that the proxy is actually being used while getting the list of proxies. + // Touch is used to inform the provider that the proxy is actually being used while getting the list of proxies. // Commonly used in DialContext and DialPacketConn - ProxiesWithTouch() []constant.Proxy + Touch() HealthCheck() } From c2469162fb1393245612be8d3fcb22d836c0f364 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Fri, 4 Nov 2022 13:28:51 +0800 Subject: [PATCH 071/250] Chore: update dependencies --- go.mod | 27 +++++++++++++------------- go.sum | 60 +++++++++++++++++++++++++++++++--------------------------- 2 files changed, 45 insertions(+), 42 deletions(-) diff --git a/go.mod b/go.mod index b4d5aeaa..25b2e1bd 100644 --- a/go.mod +++ b/go.mod @@ -6,36 +6,35 @@ require ( github.com/go-chi/chi/v5 v5.0.7 github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.2 - github.com/gofrs/uuid v4.2.0+incompatible + github.com/gofrs/uuid v4.3.1+incompatible github.com/gorilla/websocket v1.5.0 - github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84 - github.com/mdlayher/netlink v1.6.0 + github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c + github.com/mdlayher/netlink v1.6.2 github.com/miekg/dns v1.1.50 github.com/oschwald/geoip2-golang v1.8.0 github.com/sirupsen/logrus v1.9.0 - github.com/stretchr/testify v1.8.0 + github.com/stretchr/testify v1.8.1 go.etcd.io/bbolt v1.3.6 go.uber.org/atomic v1.10.0 go.uber.org/automaxprocs v1.5.1 - golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503 - golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c - golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde - golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 + golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077 + golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0 + golang.org/x/sync v0.1.0 + golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06 gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/ajg/form v1.5.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/google/go-cmp v0.5.7 // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/josharian/native v1.0.0 // indirect github.com/kr/text v0.2.0 // indirect - github.com/mdlayher/socket v0.1.1 // indirect + github.com/mdlayher/socket v0.2.3 // indirect github.com/oschwald/maxminddb-golang v1.10.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect - golang.org/x/mod v0.4.2 // indirect - golang.org/x/text v0.3.7 // indirect - golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect + golang.org/x/text v0.4.0 // indirect + golang.org/x/tools v0.1.12 // indirect ) diff --git a/go.sum b/go.sum index 4dc43b1f..aac680fc 100644 --- a/go.sum +++ b/go.sum @@ -11,22 +11,22 @@ github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg= github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= -github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= -github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI= +github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= -github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84 h1:MJTy6H+EpXLeAn0P5WAWeLk6dJA3V0ik6S3VJfUyQuI= -github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= +github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c h1:OCFM4+DXTWfNlyeoddrTwdup/ztkGSyAMR2UGcPckNQ= +github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk= github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= @@ -42,12 +42,12 @@ github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= -github.com/mdlayher/netlink v1.6.0 h1:rOHX5yl7qnlpiVkFWoqccueppMtXzeziFjWAjLg6sz0= -github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA= +github.com/mdlayher/netlink v1.6.2 h1:D2zGSkvYsJ6NreeED3JiVTu1lj2sIYATqSaZlhPzUgQ= +github.com/mdlayher/netlink v1.6.2/go.mod h1:O1HXX2sIWSMJ3Qn1BYZk1yZM+7iMki/uYGGiwGyq/iU= github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= -github.com/mdlayher/socket v0.1.1 h1:q3uOGirUPfAV2MUoaC7BavjQ154J7+JOkTWyiV+intI= -github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs= +github.com/mdlayher/socket v0.2.3 h1:XZA2X2TjdOwNoNPVPclRCURoX/hokBY8nkTmRZFEheM= +github.com/mdlayher/socket v0.2.3/go.mod h1:bz12/FozYNH/VbvC3q7TRIK/Y6dH1kCKsXaUeXi/FmY= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs= @@ -63,11 +63,13 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA= github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= @@ -80,13 +82,15 @@ go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66 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.0.0-20220824171710-5757bc0c5503 h1:vJ2V3lFLg+bBhgroYuRfyN583UzVveQmIXjc8T/y3to= -golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= +golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077 h1:t5bjOfJPQfaG9NV1imLZM5E2uzaLGs5/NtyMtRNVjQ4= +golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -96,14 +100,14 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c h1:JVAXQ10yGGVbSyoer5VILysz6YKjdNT2bsvlayjqhes= -golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0 h1:vZ44Ys50wUISbPd+jC8cRLSvhyfX9Ii/ZmDnn/aiJtM= +golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc= -golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -124,28 +128,28 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM= -golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06 h1:E1pm64FqQa4v8dHd/bAneyMkR4hk8LTJhoSlc5mc1cM= +golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 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.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 h1:BonxutuHCTL0rBDnZlKjpGIQFTjyUVTexFOdWkB6Fg0= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 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= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= From de264c42a811b609a63423d99945543c6ae1f1aa Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Fri, 4 Nov 2022 13:31:20 +0800 Subject: [PATCH 072/250] Chore: update test dependencies --- test/go.mod | 27 ++++++++++++------------ test/go.sum | 60 ++++++++++++++++++++++++++++------------------------- 2 files changed, 45 insertions(+), 42 deletions(-) diff --git a/test/go.mod b/test/go.mod index db218d21..cad844ec 100644 --- a/test/go.mod +++ b/test/go.mod @@ -7,8 +7,8 @@ require ( github.com/docker/docker v20.10.17+incompatible github.com/docker/go-connections v0.4.0 github.com/miekg/dns v1.1.50 - github.com/stretchr/testify v1.8.0 - golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c + github.com/stretchr/testify v1.8.1 + golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0 ) replace github.com/Dreamacro/clash => ../ @@ -18,14 +18,14 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/distribution v2.8.1+incompatible // indirect github.com/docker/go-units v0.4.0 // indirect - github.com/gofrs/uuid v4.2.0+incompatible // indirect + github.com/gofrs/uuid v4.3.1+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/google/go-cmp v0.5.7 // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84 // indirect + github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c // indirect github.com/josharian/native v1.0.0 // indirect - github.com/mdlayher/netlink v1.6.0 // indirect - github.com/mdlayher/socket v0.1.1 // indirect + github.com/mdlayher/netlink v1.6.2 // indirect + github.com/mdlayher/socket v0.2.3 // indirect github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect @@ -38,14 +38,13 @@ require ( github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect go.etcd.io/bbolt v1.3.6 // indirect go.uber.org/atomic v1.10.0 // indirect - golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503 // indirect - golang.org/x/mod v0.4.2 // indirect - golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde // indirect - golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077 // indirect + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06 // indirect + golang.org/x/text v0.4.0 // indirect golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect - golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + golang.org/x/tools v0.1.12 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.1.0 // indirect diff --git a/test/go.sum b/test/go.sum index 5a5557d0..d1ddf83f 100644 --- a/test/go.sum +++ b/test/go.sum @@ -15,8 +15,8 @@ github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5Xh github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= -github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0= -github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI= +github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -25,15 +25,15 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= -github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84 h1:MJTy6H+EpXLeAn0P5WAWeLk6dJA3V0ik6S3VJfUyQuI= -github.com/insomniacslk/dhcp v0.0.0-20220822114210-de18a9d48e84/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= +github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c h1:OCFM4+DXTWfNlyeoddrTwdup/ztkGSyAMR2UGcPckNQ= +github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk= github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= @@ -53,12 +53,12 @@ github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= -github.com/mdlayher/netlink v1.6.0 h1:rOHX5yl7qnlpiVkFWoqccueppMtXzeziFjWAjLg6sz0= -github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA= +github.com/mdlayher/netlink v1.6.2 h1:D2zGSkvYsJ6NreeED3JiVTu1lj2sIYATqSaZlhPzUgQ= +github.com/mdlayher/netlink v1.6.2/go.mod h1:O1HXX2sIWSMJ3Qn1BYZk1yZM+7iMki/uYGGiwGyq/iU= github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= -github.com/mdlayher/socket v0.1.1 h1:q3uOGirUPfAV2MUoaC7BavjQ154J7+JOkTWyiV+intI= -github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs= +github.com/mdlayher/socket v0.2.3 h1:XZA2X2TjdOwNoNPVPclRCURoX/hokBY8nkTmRZFEheM= +github.com/mdlayher/socket v0.2.3/go.mod h1:bz12/FozYNH/VbvC3q7TRIK/Y6dH1kCKsXaUeXi/FmY= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= @@ -86,12 +86,14 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA= github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -104,15 +106,17 @@ go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 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.0.0-20220824171710-5757bc0c5503 h1:vJ2V3lFLg+bBhgroYuRfyN583UzVveQmIXjc8T/y3to= -golang.org/x/crypto v0.0.0-20220824171710-5757bc0c5503/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077 h1:t5bjOfJPQfaG9NV1imLZM5E2uzaLGs5/NtyMtRNVjQ4= +golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= 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.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -124,16 +128,16 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c h1:JVAXQ10yGGVbSyoer5VILysz6YKjdNT2bsvlayjqhes= -golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0 h1:vZ44Ys50wUISbPd+jC8cRLSvhyfX9Ii/ZmDnn/aiJtM= +golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= 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.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde h1:ejfdSekXMDxDLbRrJMwUk6KnSLZ2McaUCVcIKM+N6jc= -golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -158,19 +162,19 @@ golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM= -golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06 h1:E1pm64FqQa4v8dHd/bAneyMkR4hk8LTJhoSlc5mc1cM= +golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 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.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 h1:M73Iuj3xbbb9Uk1DYhzydthsj6oOd6l9bpuFcNoUvTs= golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -180,12 +184,12 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn 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.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 h1:BonxutuHCTL0rBDnZlKjpGIQFTjyUVTexFOdWkB6Fg0= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 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= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= From 90f6cc233cb7412e7ae12404c370c75f2c087eb7 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 4 Nov 2022 17:38:24 +0800 Subject: [PATCH 073/250] fix: correct yaml config name --- config/config.go | 28 ++++++++++++++-------------- hub/route/configs.go | 28 ++++++++++++++-------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/config/config.go b/config/config.go index e0efcf7d..57bf55e8 100644 --- a/config/config.go +++ b/config/config.go @@ -121,20 +121,20 @@ type Tun struct { RedirectToTun []string `yaml:"-" json:"-"` MTU uint32 `yaml:"mtu" json:"mtu,omitempty"` - Inet4Address []ListenPrefix `yaml:"inet4-address" json:"inet4_address,omitempty"` - Inet6Address []ListenPrefix `yaml:"inet6-address" json:"inet6_address,omitempty"` - StrictRoute bool `yaml:"strict-route" json:"strict_route,omitempty"` - Inet4RouteAddress []ListenPrefix `yaml:"inet4_route_address" json:"inet4_route_address,omitempty"` - Inet6RouteAddress []ListenPrefix `yaml:"inet6_route_address" json:"inet6_route_address,omitempty"` - IncludeUID []uint32 `yaml:"include-uid" json:"include_uid,omitempty"` - IncludeUIDRange []string `yaml:"include-uid-range" json:"include_uid_range,omitempty"` - ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude_uid,omitempty"` - ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude_uid_range,omitempty"` - IncludeAndroidUser []int `yaml:"include-android-user" json:"include_android_user,omitempty"` - IncludePackage []string `yaml:"include-package" json:"include_package,omitempty"` - ExcludePackage []string `yaml:"exclude-package" json:"exclude_package,omitempty"` - EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint_independent_nat,omitempty"` - UDPTimeout int64 `yaml:"udp-timeout" json:"udp_timeout,omitempty"` + Inet4Address []ListenPrefix `yaml:"inet4-address" json:"inet4-address,omitempty"` + Inet6Address []ListenPrefix `yaml:"inet6-address" json:"inet6-address,omitempty"` + StrictRoute bool `yaml:"strict-route" json:"strict-route,omitempty"` + Inet4RouteAddress []ListenPrefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"` + Inet6RouteAddress []ListenPrefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"` + IncludeUID []uint32 `yaml:"include-uid" json:"include-uid,omitempty"` + IncludeUIDRange []string `yaml:"include-uid-range" json:"include-uid-range,omitempty"` + ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"` + ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"` + IncludeAndroidUser []int `yaml:"include-android-user" json:"include-android-user,omitempty"` + IncludePackage []string `yaml:"include-package" json:"include-package,omitempty"` + ExcludePackage []string `yaml:"exclude-package" json:"exclude-package,omitempty"` + EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"` + UDPTimeout int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"` } type ListenPrefix netip.Prefix diff --git a/hub/route/configs.go b/hub/route/configs.go index 773b7372..78c631ea 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -61,20 +61,20 @@ type tunSchema struct { //RedirectToTun []string `yaml:"-" json:"-"` MTU *uint32 `yaml:"mtu" json:"mtu,omitempty"` - //Inet4Address *[]config.ListenPrefix `yaml:"inet4-address" json:"inet4_address,omitempty"` - Inet6Address *[]config.ListenPrefix `yaml:"inet6-address" json:"inet6_address,omitempty"` - StrictRoute *bool `yaml:"strict-route" json:"strict_route,omitempty"` - Inet4RouteAddress *[]config.ListenPrefix `yaml:"inet4_route_address" json:"inet4_route_address,omitempty"` - Inet6RouteAddress *[]config.ListenPrefix `yaml:"inet6_route_address" json:"inet6_route_address,omitempty"` - IncludeUID *[]uint32 `yaml:"include-uid" json:"include_uid,omitempty"` - IncludeUIDRange *[]string `yaml:"include-uid-range" json:"include_uid_range,omitempty"` - ExcludeUID *[]uint32 `yaml:"exclude-uid" json:"exclude_uid,omitempty"` - ExcludeUIDRange *[]string `yaml:"exclude-uid-range" json:"exclude_uid_range,omitempty"` - IncludeAndroidUser *[]int `yaml:"include-android-user" json:"include_android_user,omitempty"` - IncludePackage *[]string `yaml:"include-package" json:"include_package,omitempty"` - ExcludePackage *[]string `yaml:"exclude-package" json:"exclude_package,omitempty"` - EndpointIndependentNat *bool `yaml:"endpoint-independent-nat" json:"endpoint_independent_nat,omitempty"` - UDPTimeout *int64 `yaml:"udp-timeout" json:"udp_timeout,omitempty"` + //Inet4Address *[]config.ListenPrefix `yaml:"inet4-address" json:"inet4-address,omitempty"` + Inet6Address *[]config.ListenPrefix `yaml:"inet6-address" json:"inet6-address,omitempty"` + StrictRoute *bool `yaml:"strict-route" json:"strict-route,omitempty"` + Inet4RouteAddress *[]config.ListenPrefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"` + Inet6RouteAddress *[]config.ListenPrefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"` + IncludeUID *[]uint32 `yaml:"include-uid" json:"include-uid,omitempty"` + IncludeUIDRange *[]string `yaml:"include-uid-range" json:"include-uid-range,omitempty"` + ExcludeUID *[]uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"` + ExcludeUIDRange *[]string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"` + IncludeAndroidUser *[]int `yaml:"include-android-user" json:"include-android-user,omitempty"` + IncludePackage *[]string `yaml:"include-package" json:"include-package,omitempty"` + ExcludePackage *[]string `yaml:"exclude-package" json:"exclude-package,omitempty"` + EndpointIndependentNat *bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"` + UDPTimeout *int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"` } func getConfigs(w http.ResponseWriter, r *http.Request) { From 52f4cb599a5a33a03913c9c964123486737224b0 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 5 Nov 2022 13:08:50 +0800 Subject: [PATCH 074/250] fix: pool_test.go --- component/fakeip/pool_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/component/fakeip/pool_test.go b/component/fakeip/pool_test.go index 0e9dd630..7919675b 100644 --- a/component/fakeip/pool_test.go +++ b/component/fakeip/pool_test.go @@ -128,8 +128,8 @@ func TestPool_CycleUsed(t *testing.T) { func TestPool_Skip(t *testing.T) { ipnet := netip.MustParsePrefix("192.168.0.1/29") - tree := trie.New[bool]() - tree.Insert("example.com", true) + tree := trie.New[struct{}]() + tree.Insert("example.com", struct{}{}) pools, tempfile, err := createPools(Options{ IPNet: &ipnet, Size: 10, From 4c5853e5e79a0cc555aac4d504c0b0a90defdc73 Mon Sep 17 00:00:00 2001 From: H1JK Date: Sat, 5 Nov 2022 06:41:07 +0000 Subject: [PATCH 075/250] feat: Converter Shadowsocks UoT support --- common/convert/converter.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/common/convert/converter.go b/common/convert/converter.go index 238e241b..24043a41 100644 --- a/common/convert/converter.go +++ b/common/convert/converter.go @@ -287,7 +287,7 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { } } - ss := make(map[string]any, 20) + ss := make(map[string]any, 10) ss["name"] = name ss["type"] = scheme @@ -297,6 +297,9 @@ func ConvertsV2Ray(buf []byte) ([]map[string]any, error) { ss["password"] = password query := urlSS.Query() ss["udp"] = true + if query.Get("udp-over-tcp") == "true" || query.Get("uot") == "1" { + ss["udp-over-tcp"] = true + } if strings.Contains(query.Get("plugin"), "obfs") { obfsParams := strings.Split(query.Get("plugin"), ";") ss["plugin"] = "obfs" From dcd2417fcec88e9bb066f81aa47b7bdf63dfb0e5 Mon Sep 17 00:00:00 2001 From: adlyq Date: Sat, 5 Nov 2022 02:24:08 +0800 Subject: [PATCH 076/250] feat: subscriptionInfo --- adapter/provider/provider.go | 58 +++++++++++++++++++++++---- adapter/provider/subscription_info.go | 54 +++++++++++++++++++++++++ component/resource/fetcher.go | 4 ++ component/resource/vehicle.go | 4 ++ 4 files changed, 112 insertions(+), 8 deletions(-) create mode 100644 adapter/provider/subscription_info.go diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index ebacf291..6656a0f3 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -5,8 +5,12 @@ import ( "errors" "fmt" "github.com/Dreamacro/clash/common/convert" + netHttp "github.com/Dreamacro/clash/component/http" "github.com/Dreamacro/clash/component/resource" + "github.com/Dreamacro/clash/log" "github.com/dlclark/regexp2" + "golang.org/x/net/context" + "net/http" "runtime" "strings" "time" @@ -33,18 +37,20 @@ type ProxySetProvider struct { type proxySetProvider struct { *resource.Fetcher[[]C.Proxy] - proxies []C.Proxy - healthCheck *HealthCheck - version uint32 + proxies []C.Proxy + healthCheck *HealthCheck + version uint32 + subscriptionInfo *SubscriptionInfo } func (pp *proxySetProvider) MarshalJSON() ([]byte, error) { return json.Marshal(map[string]any{ - "name": pp.Name(), - "type": pp.Type().String(), - "vehicleType": pp.VehicleType().String(), - "proxies": pp.Proxies(), - "updatedAt": pp.UpdatedAt, + "name": pp.Name(), + "type": pp.Type().String(), + "vehicleType": pp.VehicleType().String(), + "proxies": pp.Proxies(), + "updatedAt": pp.UpdatedAt, + "subscriptionInfo": pp.subscriptionInfo, }) } @@ -97,6 +103,40 @@ func (pp *proxySetProvider) setProxies(proxies []C.Proxy) { } } +func (pp *proxySetProvider) getSubscriptionInfo() { + if pp.VehicleType() != types.HTTP { + return + } + go func() { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) + defer cancel() + resp, err := netHttp.HttpRequest(ctx, pp.Vehicle().(*resource.HTTPVehicle).Url(), + http.MethodGet, http.Header{"User-Agent": {"clash"}}, nil) + if err != nil { + return + } + defer resp.Body.Close() + + userInfoStr := strings.TrimSpace(resp.Header.Get("subscription-userinfo")) + if userInfoStr == "" { + resp2, err := netHttp.HttpRequest(ctx, pp.Vehicle().(*resource.HTTPVehicle).Url(), + http.MethodGet, http.Header{"User-Agent": {"Quantumultx"}}, nil) + if err != nil { + return + } + defer resp2.Body.Close() + userInfoStr = strings.TrimSpace(resp2.Header.Get("subscription-userinfo")) + if userInfoStr == "" { + return + } + } + pp.subscriptionInfo, err = NewSubscriptionInfo(userInfoStr) + if err != nil { + log.Warnln("[Provider] get subscription-userinfo: %e", err) + } + }() +} + func stopProxyProvider(pd *ProxySetProvider) { pd.healthCheck.close() _ = pd.Fetcher.Destroy() @@ -128,6 +168,7 @@ func NewProxySetProvider(name string, interval time.Duration, filter string, exc fetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, proxiesParseAndFilter(filter, excludeFilter, filterRegs, excludeFilterReg), proxiesOnUpdate(pd)) pd.Fetcher = fetcher + pd.getSubscriptionInfo() wrapper := &ProxySetProvider{pd} runtime.SetFinalizer(wrapper, stopProxyProvider) return wrapper, nil @@ -218,6 +259,7 @@ func proxiesOnUpdate(pd *proxySetProvider) func([]C.Proxy) { return func(elm []C.Proxy) { pd.setProxies(elm) pd.version += 1 + pd.getSubscriptionInfo() } } diff --git a/adapter/provider/subscription_info.go b/adapter/provider/subscription_info.go new file mode 100644 index 00000000..b9fc763f --- /dev/null +++ b/adapter/provider/subscription_info.go @@ -0,0 +1,54 @@ +package provider + +import ( + "github.com/dlclark/regexp2" + "strconv" + "strings" +) + +type SubscriptionInfo struct { + Upload *int + Download *int + Total *int + Expire *int +} + +func NewSubscriptionInfo(str string) (si *SubscriptionInfo, err error) { + si = &SubscriptionInfo{} + str = strings.ToLower(str) + reTraffic := regexp2.MustCompile("upload=(\\d+); download=(\\d+); total=(\\d+)", 0) + reExpire := regexp2.MustCompile("expire=(\\d+)", 0) + + match, err := reTraffic.FindStringMatch(str) + if err != nil || match == nil { + return nil, err + } + group := match.Groups() + tmp, err := strconv.Atoi(group[1].String()) + if err != nil { + return nil, err + } + si.Upload = &tmp + tmp, err = strconv.Atoi(group[2].String()) + if err != nil { + return nil, err + } + si.Download = &tmp + tmp, err = strconv.Atoi(group[3].String()) + if err != nil { + return nil, err + } + si.Total = &tmp + + match, _ = reExpire.FindStringMatch(str) + if match != nil { + group = match.Groups() + tmp, err = strconv.Atoi(group[1].String()) + if err != nil { + return nil, err + } + si.Expire = &tmp + } + + return +} diff --git a/component/resource/fetcher.go b/component/resource/fetcher.go index 529e01b7..df8e9a54 100644 --- a/component/resource/fetcher.go +++ b/component/resource/fetcher.go @@ -35,6 +35,10 @@ func (f *Fetcher[V]) Name() string { return f.name } +func (f *Fetcher[V]) Vehicle() types.Vehicle { + return f.vehicle +} + func (f *Fetcher[V]) VehicleType() types.VehicleType { return f.vehicle.Type() } diff --git a/component/resource/vehicle.go b/component/resource/vehicle.go index c6e92e52..5e05a403 100644 --- a/component/resource/vehicle.go +++ b/component/resource/vehicle.go @@ -35,6 +35,10 @@ type HTTPVehicle struct { path string } +func (h *HTTPVehicle) Url() string { + return h.url +} + func (h *HTTPVehicle) Type() types.VehicleType { return types.HTTP } From 94a765ee314044cff635629500991672a2a17f56 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 5 Nov 2022 20:51:08 +0800 Subject: [PATCH 077/250] fix: avoid choose ZeroTier's tap to defaultInterface --- go.mod | 2 ++ go.sum | 4 ++-- listener/sing_tun/server.go | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 580481f1..c7c5a343 100644 --- a/go.mod +++ b/go.mod @@ -42,6 +42,8 @@ require ( replace github.com/lucas-clemente/quic-go => github.com/tobyxdd/quic-go v0.28.1-0.20220706211558-7780039ad599 +replace github.com/sagernet/sing-tun => github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc + require ( github.com/ajg/form v1.5.1 // indirect github.com/cheekybits/genny v1.0.0 // indirect diff --git a/go.sum b/go.sum index 6c5279d7..6c32b4ae 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,8 @@ dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1 dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc h1:B1vmR4cwDjJQ6YM8NkuXmt9vIYOdV/+qA+F3UAFE3YY= +github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc/go.mod h1:1u3pjXA9HmH7kRiBJqM3C/zPxrxnCLd3svmqtub/RFU= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= @@ -187,8 +189,6 @@ github.com/sagernet/sing v0.0.0-20220929000216-9a83e35b7186 h1:ZDlgH6dTozS3ODaYq github.com/sagernet/sing v0.0.0-20220929000216-9a83e35b7186/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4= github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4= github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM= -github.com/sagernet/sing-tun v0.0.0-20221012082254-488c3b75f6fd h1:TtoZDwg09Cpqi+gCmCtL6w4oEUZ5lHz+vHIjdr1UBNY= -github.com/sagernet/sing-tun v0.0.0-20221012082254-488c3b75f6fd/go.mod h1:1u3pjXA9HmH7kRiBJqM3C/zPxrxnCLd3svmqtub/RFU= github.com/sagernet/sing-vmess v0.0.0-20220921140858-b6a1bdee672f h1:xyJ3Wbibcug4DxLi/FCHX2Td667SfieyZv645b8+eEE= github.com/sagernet/sing-vmess v0.0.0-20220921140858-b6a1bdee672f/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 42e31c09..9d010fd4 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -210,7 +210,7 @@ func New(options config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P return } - l.openAndroidHotspot(tunOptions) + //l.openAndroidHotspot(tunOptions) log.Infoln("[TUN] Tun adapter listening at: %s(%s,%s), mtu: %d, auto route: %v, ip stack: %s", tunName, tunOptions.Inet4Address, tunOptions.Inet6Address, tunMTU, options.AutoRoute, options.Stack) From 4673d2093ab46f0a0cc2c3cf5ab580adcd331876 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 6 Nov 2022 08:43:39 +0800 Subject: [PATCH 078/250] fix: context import --- adapter/provider/provider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index 6656a0f3..b5969071 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -1,6 +1,7 @@ package provider import ( + "context" "encoding/json" "errors" "fmt" @@ -9,7 +10,6 @@ import ( "github.com/Dreamacro/clash/component/resource" "github.com/Dreamacro/clash/log" "github.com/dlclark/regexp2" - "golang.org/x/net/context" "net/http" "runtime" "strings" From bd526ad0a1480360e06ed49526f7cd28701a8dff Mon Sep 17 00:00:00 2001 From: cubemaze <117334465+cubemaze@users.noreply.github.com> Date: Mon, 7 Nov 2022 18:08:33 +0800 Subject: [PATCH 079/250] chore: adjust tun config Update config.yaml chore: adjust tun demo --- docs/config.yaml | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/docs/config.yaml b/docs/config.yaml index 61fb319a..6c8c7c26 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -40,12 +40,40 @@ hosts: # Tun 配置 tun: enable: false - stack: system # gvisor + stack: system # gvisor / lwip dns-hijack: - 198.18.0.2:53 # 需要劫持的 DNS # auto-detect-interface: true # 自动识别出口网卡 # auto-route: true # 配置路由表 - + # mtu: 9000 # 最大传输单元 + # strict_route: true # 将所有连接路由到tun来防止泄漏,但你的设备将无法其他设备被访问 + inet4_route_address: # 启用 auto_route 时使用自定义路由而不是默认路由 + - 0.0.0.0/1 + - 128.0.0.0/1 + inet6_route_address: # 启用 auto_route 时使用自定义路由而不是默认路由 + - '::/1' + - '8000::/1' + # endpoint_independent_nat: false # 启用独立于端点的 NAT + # include_uid: # UID 规则仅在 Linux 下被支持,并且需要 auto_route + # - 0 + # include_uid_range: # 限制被路由的的用户范围 + # - 1000-99999 + # exclude_uid: # 排除路由的的用户 + #- 1000 + # exclude_uid_range: # 排除路由的的用户范围 + # - 1000-99999 + + # Android 用户和应用规则仅在 Android 下被支持 + # 并且需要 auto_route + + # include_android_user: # 限制被路由的 Android 用户 + # - 0 + # - 10 + # include_package: # 限制被路由的 Android 应用包名 + # - com.android.chrome + # exclude_package: # 排除被路由的 Android 应用包名 + # - com.android.captiveportallogin + #ebpf配置 ebpf: auto-redir: # redirect 模式,仅支持 TCP From 409cd4f6a10fd35c1fb546ccded3add9130cfe22 Mon Sep 17 00:00:00 2001 From: adlyq Date: Mon, 7 Nov 2022 22:34:30 +0800 Subject: [PATCH 080/250] fix: subscriptionInfo api fix: subscriptionInfo api fix: subscriptionInfo api --- adapter/provider/subscription_info.go | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/adapter/provider/subscription_info.go b/adapter/provider/subscription_info.go index b9fc763f..2f6f5446 100644 --- a/adapter/provider/subscription_info.go +++ b/adapter/provider/subscription_info.go @@ -7,10 +7,10 @@ import ( ) type SubscriptionInfo struct { - Upload *int - Download *int - Total *int - Expire *int + Upload uint64 + Download uint64 + Total uint64 + Expire uint64 } func NewSubscriptionInfo(str string) (si *SubscriptionInfo, err error) { @@ -24,31 +24,34 @@ func NewSubscriptionInfo(str string) (si *SubscriptionInfo, err error) { return nil, err } group := match.Groups() - tmp, err := strconv.Atoi(group[1].String()) + si.Upload, err = str2uint64(group[1].String()) if err != nil { return nil, err } - si.Upload = &tmp - tmp, err = strconv.Atoi(group[2].String()) + + si.Download, err = str2uint64(group[2].String()) if err != nil { return nil, err } - si.Download = &tmp - tmp, err = strconv.Atoi(group[3].String()) + + si.Total, err = str2uint64(group[3].String()) if err != nil { return nil, err } - si.Total = &tmp match, _ = reExpire.FindStringMatch(str) if match != nil { group = match.Groups() - tmp, err = strconv.Atoi(group[1].String()) + si.Expire, err = str2uint64(group[1].String()) if err != nil { return nil, err } - si.Expire = &tmp } return } + +func str2uint64(str string) (uint64, error) { + i, err := strconv.ParseInt(str, 10, 64) + return uint64(i), err +} From 2d3aad573e8ce9b72e19fabe8e89dcaeaf17f37f Mon Sep 17 00:00:00 2001 From: Skimmle Date: Tue, 8 Nov 2022 15:50:01 +0800 Subject: [PATCH 081/250] chore: upgrade dependencies --- go.mod | 17 ++-- go.sum | 247 +++++---------------------------------------------------- 2 files changed, 25 insertions(+), 239 deletions(-) diff --git a/go.mod b/go.mod index c7c5a343..e8ef2549 100644 --- a/go.mod +++ b/go.mod @@ -40,25 +40,23 @@ require ( ) -replace github.com/lucas-clemente/quic-go => github.com/tobyxdd/quic-go v0.28.1-0.20220706211558-7780039ad599 +replace github.com/lucas-clemente/quic-go => github.com/HyNetwork/quic-go v0.30.1-0.20221105180419-83715d7269a8 replace github.com/sagernet/sing-tun => github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc require ( github.com/ajg/form v1.5.1 // indirect - github.com/cheekybits/genny v1.0.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect + github.com/golang/mock v1.6.0 // indirect github.com/google/btree v1.1.2 // indirect + github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/klauspost/cpuid/v2 v2.1.1 // indirect - github.com/marten-seemann/qpack v0.2.1 // indirect - github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect - github.com/marten-seemann/qtls-go1-17 v0.1.2 // indirect - github.com/marten-seemann/qtls-go1-18 v0.1.2 // indirect - github.com/marten-seemann/qtls-go1-19 v0.1.0 // indirect - github.com/nxadm/tail v1.4.8 // indirect - github.com/onsi/ginkgo v1.16.5 // indirect + github.com/marten-seemann/qpack v0.3.0 // indirect + github.com/marten-seemann/qtls-go1-18 v0.1.3 // indirect + github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect + github.com/onsi/ginkgo/v2 v2.2.0 // indirect github.com/oschwald/maxminddb-golang v1.10.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e // indirect @@ -70,7 +68,6 @@ require ( golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect golang.org/x/tools v0.1.12 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect - gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c // indirect lukechampine.com/blake3 v1.1.7 // indirect ) diff --git a/go.sum b/go.sum index 6c32b4ae..86f8a3e5 100644 --- a/go.sum +++ b/go.sum @@ -1,29 +1,16 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= -dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= -dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= -dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= -dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= -git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/HyNetwork/quic-go v0.30.1-0.20221105180419-83715d7269a8 h1:FBo40lMrk1bZZzJRJx8U+bQUPhLDGTUJ/Q5NV5BbO4Q= +github.com/HyNetwork/quic-go v0.30.1-0.20221105180419-83715d7269a8/go.mod h1:ssOrRsOmdxa768Wr78vnh2B8JozgLsMzG/g+0qEC7uk= github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc h1:B1vmR4cwDjJQ6YM8NkuXmt9vIYOdV/+qA+F3UAFE3YY= github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc/go.mod h1:1u3pjXA9HmH7kRiBJqM3C/zPxrxnCLd3svmqtub/RFU= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= -github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= -github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= +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.9.3 h1:5KtxXZU+scyERvkJMEm16TbScVvuuMrlhPly78ZMbSc= github.com/cilium/ebpf v0.9.3/go.mod h1:w27N4UjpaQ9X/DGrSugxUG+H+NhgntDuPb5lCzxCn8A= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk= github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= -github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/database64128/tfo-go v1.1.2 h1:GwxtJp09BdUTVEoeT421t231eNZoGOCRkklbl4WI1kU= github.com/database64128/tfo-go v1.1.2/go.mod h1:jgrSUPyOvTGQyn6irCOpk7L2W/q/0VLZZcovQiMi+bI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -31,48 +18,24 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo= github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8= github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg= github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gofrs/uuid v4.3.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc= github.com/gofrs/uuid v4.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 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.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -82,59 +45,37 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= -github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c h1:OCFM4+DXTWfNlyeoddrTwdup/ztkGSyAMR2UGcPckNQ= github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= -github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.1.1 h1:t0wUqjowdm8ezddV5k0tLWVklVuvLJpoHeb4WBdydm0= github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg= -github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/marten-seemann/qpack v0.2.1 h1:jvTsT/HpCn2UZJdP+UUB53FfUUgeOyG5K1ns0OJOGVs= -github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= -github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ= -github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk= -github.com/marten-seemann/qtls-go1-17 v0.1.2 h1:JADBlm0LYiVbuSySCHeY863dNkcpMmDR7s0bLKJeYlQ= -github.com/marten-seemann/qtls-go1-17 v0.1.2/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s= -github.com/marten-seemann/qtls-go1-18 v0.1.2 h1:JH6jmzbduz0ITVQ7ShevK10Av5+jBEKAHMntXmIV7kM= -github.com/marten-seemann/qtls-go1-18 v0.1.2/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= -github.com/marten-seemann/qtls-go1-19 v0.1.0-beta.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= -github.com/marten-seemann/qtls-go1-19 v0.1.0 h1:rLFKD/9mp/uq1SYGYuVZhm83wkmU95pK5df3GufyYYU= -github.com/marten-seemann/qtls-go1-19 v0.1.0/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/marten-seemann/qpack v0.3.0 h1:UiWstOgT8+znlkDPOg2+3rIuYXJ2CnGDkGUXN6ki6hE= +github.com/marten-seemann/qpack v0.3.0/go.mod h1:cGfKPBiP4a9EQdxCwEwI/GEeWAsjSekBvx/X8mh58+g= +github.com/marten-seemann/qtls-go1-18 v0.1.3 h1:R4H2Ks8P6pAtUagjFty2p7BVHn3XiwDAl7TTQf5h7TI= +github.com/marten-seemann/qtls-go1-18 v0.1.3/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= +github.com/marten-seemann/qtls-go1-19 v0.1.1 h1:mnbxeq3oEyQxQXwI4ReCgW9DPoPR94sNlqWoDZnjRIE= +github.com/marten-seemann/qtls-go1-19 v0.1.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y= github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= @@ -142,42 +83,19 @@ github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcK github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= -github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= -github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak= -github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= -github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI= +github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= +github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs= github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw= github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg= github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= -github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e h1:5CFRo8FJbCuf5s/eTBdZpmMbn8Fe2eSMLNAYfKanA34= github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e/go.mod h1:qbt0dWObotCfcjAJJ9AxtFPNSDUfZF+6dCpgKEOBn/g= github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA= @@ -191,96 +109,45 @@ github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDe github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM= github.com/sagernet/sing-vmess v0.0.0-20220921140858-b6a1bdee672f h1:xyJ3Wbibcug4DxLi/FCHX2Td667SfieyZv645b8+eEE= github.com/sagernet/sing-vmess v0.0.0-20220921140858-b6a1bdee672f/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= -github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= -github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= -github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= -github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= -github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= -github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= -github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= -github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= -github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= -github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= -github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= -github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= -github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= -github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= -github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= -github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= -github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/tobyxdd/quic-go v0.28.1-0.20220706211558-7780039ad599 h1:We+z04jRpTGxFggeGWf+GbinhlIk1I1kMMEgujhUfiA= -github.com/tobyxdd/quic-go v0.28.1-0.20220706211558-7780039ad599/go.mod h1:oGz5DKK41cJt5+773+BSO9BXDsREY4HLf7+0odGAPO0= github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 h1:hl6sK6aFgTLISijk6xIzeqnPzQcsLqqvL6vEfTPinME= github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= -github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= -github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/vishvananda/netns v0.0.0-20220913150850-18c4f4234207 h1:nn7SOQy8xCu3iXNv7oiBhhEQtbWdnEOMnuKBlHvrqIM= github.com/vishvananda/netns v0.0.0-20220913150850-18c4f4234207/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837 h1:AHhUwwFJGl27E46OpdJHplZkK09m7aETNBNzhT6t15M= github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837/go.mod h1:YJTRELIWrGxR1s8xcEBgxcxBfwQfMGjdvNLTjN9XFgY= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/automaxprocs v1.5.1 h1:e1YG66Lrk73dn4qhg8WFSvhF0JuFQF0ERIp4rpuV8Qk= go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66vU6XU= -go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= -golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= -golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/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-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A= golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 h1:RjggHMcaTVp0LOVZcW0bo8alwHrOaCrGUDgfWUHhnN4= golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 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.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= @@ -288,94 +155,55 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20221004154528-8021a29435af h1:wv66FM3rLZGPdxpYL+ApnDe2HzHcTFta3z5nsc13wI4= golang.org/x/net v0.0.0-20221004154528-8021a29435af/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/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.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 h1:cu5kTvlzcw1Q5S9f5ip1/cpiB4nXvw1XYzFPGgzLUOY= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875 h1:AzgQNqF+FKwyQ5LbVrVqOcuuFB67N47F9+htZYH0wFM= golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab h1:eHo2TTVBaAPw9lDGK2Gb9GyPMXT6g7O63W6sx3ylbzU= golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= @@ -384,56 +212,17 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T 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= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= -google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c h1:m5lcgWnL3OElQNVyp3qcncItJ2c0sQlSGjYK2+nJTA4= gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= -sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= From 53b2a480ef3b2b73b79e431778a290b09c19bd4a Mon Sep 17 00:00:00 2001 From: metacubex Date: Tue, 8 Nov 2022 22:30:50 +0800 Subject: [PATCH 082/250] fix: subscriptionInfo api --- adapter/provider/subscription_info.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/adapter/provider/subscription_info.go b/adapter/provider/subscription_info.go index 2f6f5446..fc6992e2 100644 --- a/adapter/provider/subscription_info.go +++ b/adapter/provider/subscription_info.go @@ -7,10 +7,10 @@ import ( ) type SubscriptionInfo struct { - Upload uint64 - Download uint64 - Total uint64 - Expire uint64 + Upload int64 + Download int64 + Total int64 + Expire int64 } func NewSubscriptionInfo(str string) (si *SubscriptionInfo, err error) { @@ -51,7 +51,7 @@ func NewSubscriptionInfo(str string) (si *SubscriptionInfo, err error) { return } -func str2uint64(str string) (uint64, error) { +func str2uint64(str string) (int64, error) { i, err := strconv.ParseInt(str, 10, 64) - return uint64(i), err + return i, err } From 5fd79890e71037b58158011c1974f2c70f6281a1 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 9 Nov 2022 08:06:37 +0800 Subject: [PATCH 083/250] chore: add exclude-filter to ProxyGroup --- adapter/outboundgroup/fallback.go | 1 + adapter/outboundgroup/groupbase.go | 48 ++++++++++++++++++++-------- adapter/outboundgroup/loadbalance.go | 1 + adapter/outboundgroup/parser.go | 19 +++++------ adapter/outboundgroup/relay.go | 1 + adapter/outboundgroup/selector.go | 1 + adapter/outboundgroup/urltest.go | 1 + adapter/provider/provider.go | 6 ++-- component/resource/vehicle.go | 4 +-- 9 files changed, 54 insertions(+), 28 deletions(-) diff --git a/adapter/outboundgroup/fallback.go b/adapter/outboundgroup/fallback.go index a433d54d..0a4dab41 100644 --- a/adapter/outboundgroup/fallback.go +++ b/adapter/outboundgroup/fallback.go @@ -131,6 +131,7 @@ func NewFallback(option *GroupCommonOption, providers []provider.ProxyProvider) RoutingMark: option.RoutingMark, }, option.Filter, + option.ExcludeFilter, providers, }), disableUDP: option.DisableUDP, diff --git a/adapter/outboundgroup/groupbase.go b/adapter/outboundgroup/groupbase.go index fbe887e3..8dd57861 100644 --- a/adapter/outboundgroup/groupbase.go +++ b/adapter/outboundgroup/groupbase.go @@ -18,23 +18,30 @@ import ( type GroupBase struct { *outbound.Base - filterRegs []*regexp2.Regexp - providers []provider.ProxyProvider - failedTestMux sync.Mutex - failedTimes int - failedTime time.Time - failedTesting *atomic.Bool - proxies [][]C.Proxy - versions []atomic.Uint32 + filterRegs []*regexp2.Regexp + excludeFilterReg *regexp2.Regexp + providers []provider.ProxyProvider + failedTestMux sync.Mutex + failedTimes int + failedTime time.Time + failedTesting *atomic.Bool + proxies [][]C.Proxy + versions []atomic.Uint32 } type GroupBaseOption struct { outbound.BaseOption - filter string - providers []provider.ProxyProvider + filter string + excludeFilter string + providers []provider.ProxyProvider } func NewGroupBase(opt GroupBaseOption) *GroupBase { + var excludeFilterReg *regexp2.Regexp + if opt.excludeFilter != "" { + excludeFilterReg = regexp2.MustCompile(opt.excludeFilter, 0) + } + var filterRegs []*regexp2.Regexp if opt.filter != "" { for _, filter := range strings.Split(opt.filter, "`") { @@ -44,10 +51,11 @@ func NewGroupBase(opt GroupBaseOption) *GroupBase { } gb := &GroupBase{ - Base: outbound.NewBase(opt.BaseOption), - filterRegs: filterRegs, - providers: opt.providers, - failedTesting: atomic.NewBool(false), + Base: outbound.NewBase(opt.BaseOption), + filterRegs: filterRegs, + excludeFilterReg: excludeFilterReg, + providers: opt.providers, + failedTesting: atomic.NewBool(false), } gb.proxies = make([][]C.Proxy, len(opt.providers)) @@ -146,6 +154,18 @@ func (gb *GroupBase) GetProxies(touch bool) []C.Proxy { proxies = newProxies } + if gb.excludeFilterReg != nil { + var newProxies []C.Proxy + for _, p := range proxies { + name := p.Name() + if mat, _ := gb.excludeFilterReg.FindStringMatch(name); mat != nil { + continue + } + newProxies = append(newProxies, p) + } + proxies = newProxies + } + return proxies } diff --git a/adapter/outboundgroup/loadbalance.go b/adapter/outboundgroup/loadbalance.go index 87d7de7b..a396f420 100644 --- a/adapter/outboundgroup/loadbalance.go +++ b/adapter/outboundgroup/loadbalance.go @@ -228,6 +228,7 @@ func NewLoadBalance(option *GroupCommonOption, providers []provider.ProxyProvide RoutingMark: option.RoutingMark, }, option.Filter, + option.ExcludeFilter, providers, }), strategyFn: strategyFn, diff --git a/adapter/outboundgroup/parser.go b/adapter/outboundgroup/parser.go index b808079b..53a82a60 100644 --- a/adapter/outboundgroup/parser.go +++ b/adapter/outboundgroup/parser.go @@ -21,15 +21,16 @@ 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"` + 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"` } func ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, providersMap map[string]types.ProxyProvider) (C.ProxyAdapter, error) { diff --git a/adapter/outboundgroup/relay.go b/adapter/outboundgroup/relay.go index 729f4137..66954375 100644 --- a/adapter/outboundgroup/relay.go +++ b/adapter/outboundgroup/relay.go @@ -185,6 +185,7 @@ func NewRelay(option *GroupCommonOption, providers []provider.ProxyProvider) *Re RoutingMark: option.RoutingMark, }, "", + "", providers, }), } diff --git a/adapter/outboundgroup/selector.go b/adapter/outboundgroup/selector.go index dcf07707..71ebacce 100644 --- a/adapter/outboundgroup/selector.go +++ b/adapter/outboundgroup/selector.go @@ -99,6 +99,7 @@ func NewSelector(option *GroupCommonOption, providers []provider.ProxyProvider) RoutingMark: option.RoutingMark, }, option.Filter, + option.ExcludeFilter, providers, }), selected: "COMPATIBLE", diff --git a/adapter/outboundgroup/urltest.go b/adapter/outboundgroup/urltest.go index 1e69652c..55c1cc7c 100644 --- a/adapter/outboundgroup/urltest.go +++ b/adapter/outboundgroup/urltest.go @@ -143,6 +143,7 @@ func NewURLTest(option *GroupCommonOption, providers []provider.ProxyProvider, o }, option.Filter, + option.ExcludeFilter, providers, }), fastSingle: singledo.NewSingle[C.Proxy](time.Second * 10), diff --git a/adapter/provider/provider.go b/adapter/provider/provider.go index b5969071..6f16a813 100644 --- a/adapter/provider/provider.go +++ b/adapter/provider/provider.go @@ -6,7 +6,7 @@ import ( "errors" "fmt" "github.com/Dreamacro/clash/common/convert" - netHttp "github.com/Dreamacro/clash/component/http" + clashHttp "github.com/Dreamacro/clash/component/http" "github.com/Dreamacro/clash/component/resource" "github.com/Dreamacro/clash/log" "github.com/dlclark/regexp2" @@ -110,7 +110,7 @@ func (pp *proxySetProvider) getSubscriptionInfo() { go func() { ctx, cancel := context.WithTimeout(context.Background(), time.Second*90) defer cancel() - resp, err := netHttp.HttpRequest(ctx, pp.Vehicle().(*resource.HTTPVehicle).Url(), + resp, err := clashHttp.HttpRequest(ctx, pp.Vehicle().(*resource.HTTPVehicle).Url(), http.MethodGet, http.Header{"User-Agent": {"clash"}}, nil) if err != nil { return @@ -119,7 +119,7 @@ func (pp *proxySetProvider) getSubscriptionInfo() { userInfoStr := strings.TrimSpace(resp.Header.Get("subscription-userinfo")) if userInfoStr == "" { - resp2, err := netHttp.HttpRequest(ctx, pp.Vehicle().(*resource.HTTPVehicle).Url(), + resp2, err := clashHttp.HttpRequest(ctx, pp.Vehicle().(*resource.HTTPVehicle).Url(), http.MethodGet, http.Header{"User-Agent": {"Quantumultx"}}, nil) if err != nil { return diff --git a/component/resource/vehicle.go b/component/resource/vehicle.go index 5e05a403..927a9604 100644 --- a/component/resource/vehicle.go +++ b/component/resource/vehicle.go @@ -2,7 +2,7 @@ package resource import ( "context" - netHttp "github.com/Dreamacro/clash/component/http" + clashHttp "github.com/Dreamacro/clash/component/http" types "github.com/Dreamacro/clash/constant/provider" "io" "net/http" @@ -50,7 +50,7 @@ func (h *HTTPVehicle) Path() string { func (h *HTTPVehicle) Read() ([]byte, error) { ctx, cancel := context.WithTimeout(context.Background(), time.Second*20) defer cancel() - resp, err := netHttp.HttpRequest(ctx, h.url, http.MethodGet, nil, nil) + resp, err := clashHttp.HttpRequest(ctx, h.url, http.MethodGet, nil, nil) if err != nil { return nil, err } From 1d784231b09597d6bff17689c0149fe31bc18cb7 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 9 Nov 2022 08:41:30 +0800 Subject: [PATCH 084/250] fix: exclude-filter not work when filter is empty --- adapter/outboundgroup/groupbase.go | 69 ++++++++++++++---------------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/adapter/outboundgroup/groupbase.go b/adapter/outboundgroup/groupbase.go index 8dd57861..426a1282 100644 --- a/adapter/outboundgroup/groupbase.go +++ b/adapter/outboundgroup/groupbase.go @@ -71,59 +71,54 @@ func (gb *GroupBase) Touch() { } func (gb *GroupBase) GetProxies(touch bool) []C.Proxy { + var proxies []C.Proxy if len(gb.filterRegs) == 0 { - var proxies []C.Proxy for _, pd := range gb.providers { if touch { pd.Touch() } proxies = append(proxies, pd.Proxies()...) } - if len(proxies) == 0 { - return append(proxies, tunnel.Proxies()["COMPATIBLE"]) - } - return proxies - } + } else { + for i, pd := range gb.providers { + if touch { + pd.Touch() + } - for i, pd := range gb.providers { - if touch { - pd.Touch() - } + if pd.VehicleType() == types.Compatible { + gb.versions[i].Store(pd.Version()) + gb.proxies[i] = pd.Proxies() + continue + } - if pd.VehicleType() == types.Compatible { - gb.versions[i].Store(pd.Version()) - gb.proxies[i] = pd.Proxies() - continue - } + version := gb.versions[i].Load() + if version != pd.Version() && gb.versions[i].CompareAndSwap(version, pd.Version()) { + var ( + proxies []C.Proxy + newProxies []C.Proxy + ) - version := gb.versions[i].Load() - if version != pd.Version() && gb.versions[i].CompareAndSwap(version, pd.Version()) { - var ( - proxies []C.Proxy - newProxies []C.Proxy - ) - - proxies = pd.Proxies() - proxiesSet := map[string]struct{}{} - for _, filterReg := range gb.filterRegs { - for _, p := range proxies { - name := p.Name() - if mat, _ := filterReg.FindStringMatch(name); mat != nil { - if _, ok := proxiesSet[name]; !ok { - proxiesSet[name] = struct{}{} - newProxies = append(newProxies, p) + proxies = pd.Proxies() + proxiesSet := map[string]struct{}{} + for _, filterReg := range gb.filterRegs { + for _, p := range proxies { + name := p.Name() + if mat, _ := filterReg.FindStringMatch(name); mat != nil { + if _, ok := proxiesSet[name]; !ok { + proxiesSet[name] = struct{}{} + newProxies = append(newProxies, p) + } } } } + + gb.proxies[i] = newProxies } - - gb.proxies[i] = newProxies } - } - var proxies []C.Proxy - for _, p := range gb.proxies { - proxies = append(proxies, p...) + for _, p := range gb.proxies { + proxies = append(proxies, p...) + } } if len(proxies) == 0 { From ae08d13de4d09177553184ea434ee1ea26b4a48f Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 9 Nov 2022 18:44:06 +0800 Subject: [PATCH 085/250] chore: support wireguard outbound --- adapter/outbound/wireguard.go | 325 ++++++++++++++++++++++++++++++++++ adapter/parser.go | 7 + constant/adapters.go | 3 + go.mod | 12 +- go.sum | 24 +-- 5 files changed, 356 insertions(+), 15 deletions(-) create mode 100644 adapter/outbound/wireguard.go diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go new file mode 100644 index 00000000..fe26285e --- /dev/null +++ b/adapter/outbound/wireguard.go @@ -0,0 +1,325 @@ +package outbound + +import ( + "context" + "encoding/base64" + "encoding/hex" + "errors" + "fmt" + "net" + "net/netip" + "runtime" + "strconv" + "strings" + "sync" + "time" + + "github.com/Dreamacro/clash/component/dialer" + "github.com/Dreamacro/clash/component/resolver" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/listener/sing" + + wireguard "github.com/MetaCubeX/sing-wireguard" + + "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/debug" + E "github.com/sagernet/sing/common/exceptions" + M "github.com/sagernet/sing/common/metadata" + N "github.com/sagernet/sing/common/network" + "github.com/sagernet/wireguard-go/device" +) + +type WireGuard struct { + *Base + bind *wireguard.ClientBind + device *device.Device + tunDevice wireguard.Device + dialer *wgDialer + startOnce sync.Once +} + +type WireGuardOption struct { + BasicOption + Name string `proxy:"name"` + Server string `proxy:"server"` + Port int `proxy:"port"` + Ip string `proxy:"ip"` + Ipv6 string `proxy:"ipv6,omitempty"` + PrivateKey string `proxy:"private-key"` + PublicKey string `proxy:"public-key"` + PreSharedKey string `proxy:"pre-shared-key,omitempty"` + Reserved []int `proxy:"reserved,omitempty"` + Workers int `proxy:"workers,omitempty"` + MTU int `proxy:"mtu,omitempty"` + UDP bool `proxy:"udp,omitempty"` +} + +type wgDialer struct { + options []dialer.Option +} + +func (d *wgDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) { + return dialer.DialContext(ctx, network, destination.String(), d.options...) +} + +func (d *wgDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { + return dialer.ListenPacket(ctx, "udp", "", d.options...) +} + +func NewWireGuard(option WireGuardOption) (*WireGuard, error) { + outbound := &WireGuard{ + Base: &Base{ + name: option.Name, + addr: net.JoinHostPort(option.Server, strconv.Itoa(option.Port)), + tp: C.WireGuard, + udp: option.UDP, + iface: option.Interface, + rmark: option.RoutingMark, + prefer: C.NewDNSPrefer(option.IPVersion), + }, + dialer: &wgDialer{}, + } + runtime.SetFinalizer(outbound, closeWireGuard) + + var reserved [3]uint8 + if len(option.Reserved) > 0 { + if len(option.Reserved) != 3 { + return nil, E.New("invalid reserved value, required 3 bytes, got ", len(option.Reserved)) + } + reserved[0] = uint8(option.Reserved[0]) + reserved[1] = uint8(option.Reserved[1]) + reserved[2] = uint8(option.Reserved[2]) + } + peerAddr := M.ParseSocksaddr(option.Server) + peerAddr.Port = uint16(option.Port) + outbound.bind = wireguard.NewClientBind(context.Background(), outbound.dialer, peerAddr, reserved) + localPrefixes := make([]netip.Prefix, 0, 2) + if len(option.Ip) == 0 { + return nil, E.New("missing local address") + } + if !strings.Contains(option.Ip, "/") { + option.Ip = option.Ip + "/32" + } + if prefix, err := netip.ParsePrefix(option.Ip); err == nil { + localPrefixes = append(localPrefixes, prefix) + } else { + return nil, E.Cause(err, "ip address parse error") + } + if len(option.Ipv6) > 0 { + if !strings.Contains(option.Ipv6, "/") { + option.Ipv6 = option.Ipv6 + "/128" + } + if prefix, err := netip.ParsePrefix(option.Ipv6); err == nil { + localPrefixes = append(localPrefixes, prefix) + } else { + return nil, E.Cause(err, "ipv6 address parse error") + } + } + var privateKey, peerPublicKey, preSharedKey string + { + bytes, err := base64.StdEncoding.DecodeString(option.PrivateKey) + if err != nil { + return nil, E.Cause(err, "decode private key") + } + privateKey = hex.EncodeToString(bytes) + } + { + bytes, err := base64.StdEncoding.DecodeString(option.PublicKey) + if err != nil { + return nil, E.Cause(err, "decode peer public key") + } + peerPublicKey = hex.EncodeToString(bytes) + } + if option.PreSharedKey != "" { + bytes, err := base64.StdEncoding.DecodeString(option.PreSharedKey) + if err != nil { + return nil, E.Cause(err, "decode pre shared key") + } + preSharedKey = hex.EncodeToString(bytes) + } + ipcConf := "private_key=" + privateKey + ipcConf += "\npublic_key=" + peerPublicKey + ipcConf += "\nendpoint=" + peerAddr.String() + if preSharedKey != "" { + ipcConf += "\npreshared_key=" + preSharedKey + } + var has4, has6 bool + for _, address := range localPrefixes { + if address.Addr().Is4() { + has4 = true + } else { + has6 = true + } + } + if has4 { + ipcConf += "\nallowed_ip=0.0.0.0/0" + } + if has6 { + ipcConf += "\nallowed_ip=::/0" + } + mtu := option.MTU + if mtu == 0 { + mtu = 1408 + } + var err error + outbound.tunDevice, err = wireguard.NewStackDevice(localPrefixes, uint32(mtu)) + if err != nil { + return nil, E.Cause(err, "create WireGuard device") + } + outbound.device = device.NewDevice(outbound.tunDevice, outbound.bind, &device.Logger{ + Verbosef: func(format string, args ...interface{}) { + sing.Logger.Debug(fmt.Sprintf(strings.ToLower(format), args...)) + }, + Errorf: func(format string, args ...interface{}) { + sing.Logger.Error(fmt.Sprintf(strings.ToLower(format), args...)) + }, + }, option.Workers) + if debug.Enabled { + sing.Logger.Trace("created wireguard ipc conf: \n", ipcConf) + } + err = outbound.device.IpcSet(ipcConf) + if err != nil { + return nil, E.Cause(err, "setup wireguard") + } + //err = outbound.tunDevice.Start() + return outbound, nil +} + +func closeWireGuard(w *WireGuard) { + if w.device != nil { + w.device.Close() + } + _ = common.Close(w.tunDevice) +} + +func (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { + w.dialer.options = opts + var conn net.Conn + w.startOnce.Do(func() { + err = w.tunDevice.Start() + }) + if err != nil { + return nil, err + } + if !metadata.Resolved() { + addrs, err := resolver.ResolveAllIP(metadata.Host) + if err != nil { + return nil, err + } + conn, err = N.DialSerial(ctx, w.tunDevice, "tcp", M.ParseSocksaddr(metadata.RemoteAddress()), addrs) + } else { + conn, err = w.tunDevice.DialContext(ctx, "tcp", M.ParseSocksaddr(metadata.Pure().RemoteAddress())) + } + if err != nil { + return nil, err + } + return NewConn(&wgConn{conn, w}, w), nil +} + +func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { + w.dialer.options = opts + var pc net.PacketConn + w.startOnce.Do(func() { + err = w.tunDevice.Start() + }) + if err != nil { + return nil, err + } + if !metadata.Resolved() { + ip, err := resolver.ResolveIP(metadata.Host) + if err != nil { + return nil, errors.New("can't resolve ip") + } + metadata.DstIP = ip + } + pc, err = w.tunDevice.ListenPacket(ctx, M.ParseSocksaddr(metadata.Pure().RemoteAddress())) + if err != nil { + return nil, err + } + return newPacketConn(&wgPacketConn{pc, w}, w), nil +} + +type wgConn struct { + conn net.Conn + wg *WireGuard +} + +func (c *wgConn) Read(b []byte) (n int, err error) { + defer runtime.KeepAlive(c.wg) + return c.conn.Read(b) +} + +func (c *wgConn) Write(b []byte) (n int, err error) { + defer runtime.KeepAlive(c.wg) + return c.conn.Write(b) +} + +func (c *wgConn) Close() error { + defer runtime.KeepAlive(c.wg) + return c.conn.Close() +} + +func (c *wgConn) LocalAddr() net.Addr { + defer runtime.KeepAlive(c.wg) + return c.conn.LocalAddr() +} + +func (c *wgConn) RemoteAddr() net.Addr { + defer runtime.KeepAlive(c.wg) + return c.conn.RemoteAddr() +} + +func (c *wgConn) SetDeadline(t time.Time) error { + defer runtime.KeepAlive(c.wg) + return c.conn.SetDeadline(t) +} + +func (c *wgConn) SetReadDeadline(t time.Time) error { + defer runtime.KeepAlive(c.wg) + return c.conn.SetReadDeadline(t) +} + +func (c *wgConn) SetWriteDeadline(t time.Time) error { + defer runtime.KeepAlive(c.wg) + return c.conn.SetWriteDeadline(t) +} + +type wgPacketConn struct { + pc net.PacketConn + wg *WireGuard +} + +func (pc *wgPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + defer runtime.KeepAlive(pc.wg) + return pc.pc.ReadFrom(p) +} + +func (pc *wgPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + defer runtime.KeepAlive(pc.wg) + return pc.pc.WriteTo(p, addr) +} + +func (pc *wgPacketConn) Close() error { + defer runtime.KeepAlive(pc.wg) + return pc.pc.Close() +} + +func (pc *wgPacketConn) LocalAddr() net.Addr { + defer runtime.KeepAlive(pc.wg) + return pc.pc.LocalAddr() +} + +func (pc *wgPacketConn) SetDeadline(t time.Time) error { + defer runtime.KeepAlive(pc.wg) + return pc.pc.SetDeadline(t) +} + +func (pc *wgPacketConn) SetReadDeadline(t time.Time) error { + defer runtime.KeepAlive(pc.wg) + return pc.pc.SetReadDeadline(t) +} + +func (pc *wgPacketConn) SetWriteDeadline(t time.Time) error { + defer runtime.KeepAlive(pc.wg) + return pc.pc.SetWriteDeadline(t) +} diff --git a/adapter/parser.go b/adapter/parser.go index b68e81ab..5d145998 100644 --- a/adapter/parser.go +++ b/adapter/parser.go @@ -88,6 +88,13 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) { break } proxy, err = outbound.NewHysteria(*hyOption) + case "wireguard": + hyOption := &outbound.WireGuardOption{} + err = decoder.Decode(mapping, hyOption) + if err != nil { + break + } + proxy, err = outbound.NewWireGuard(*hyOption) default: return nil, fmt.Errorf("unsupport proxy type: %s", proxyType) } diff --git a/constant/adapters.go b/constant/adapters.go index 7fc31667..71613487 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -31,6 +31,7 @@ const ( Vless Trojan Hysteria + WireGuard ) const ( @@ -165,6 +166,8 @@ func (at AdapterType) String() string { return "Trojan" case Hysteria: return "Hysteria" + case WireGuard: + return "WireGuard" case Relay: return "Relay" diff --git a/go.mod b/go.mod index e8ef2549..9a8994f1 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/Dreamacro/clash go 1.19 require ( + github.com/MetaCubeX/sing-wireguard v0.0.0-20221109081324-ec09c458d37a github.com/cilium/ebpf v0.9.3 github.com/coreos/go-iptables v0.6.0 github.com/database64128/tfo-go v1.1.2 @@ -20,21 +21,22 @@ require ( github.com/miekg/dns v1.1.50 github.com/oschwald/geoip2-golang v1.8.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 - github.com/sagernet/sing v0.0.0-20220929000216-9a83e35b7186 + github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4 github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 github.com/sagernet/sing-tun v0.0.0-20221012082254-488c3b75f6fd github.com/sagernet/sing-vmess v0.0.0-20220921140858-b6a1bdee672f + github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.0 github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837 go.etcd.io/bbolt v1.3.6 go.uber.org/atomic v1.10.0 go.uber.org/automaxprocs v1.5.1 - golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be + golang.org/x/crypto v0.1.0 golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 - golang.org/x/net v0.0.0-20221004154528-8021a29435af + golang.org/x/net v0.1.0 golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 - golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875 + golang.org/x/sys v0.1.0 google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v3 v3.0.1 @@ -64,7 +66,7 @@ require ( github.com/u-root/uio v0.0.0-20220204230159-dac05f7d2cb4 // indirect github.com/vishvananda/netns v0.0.0-20220913150850-18c4f4234207 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect - golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab // indirect + golang.org/x/text v0.4.0 // indirect golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect golang.org/x/tools v0.1.12 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect diff --git a/go.sum b/go.sum index 86f8a3e5..48c5cecb 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ github.com/HyNetwork/quic-go v0.30.1-0.20221105180419-83715d7269a8 h1:FBo40lMrk1 github.com/HyNetwork/quic-go v0.30.1-0.20221105180419-83715d7269a8/go.mod h1:ssOrRsOmdxa768Wr78vnh2B8JozgLsMzG/g+0qEC7uk= github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc h1:B1vmR4cwDjJQ6YM8NkuXmt9vIYOdV/+qA+F3UAFE3YY= github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc/go.mod h1:1u3pjXA9HmH7kRiBJqM3C/zPxrxnCLd3svmqtub/RFU= +github.com/MetaCubeX/sing-wireguard v0.0.0-20221109081324-ec09c458d37a h1:PG0LSxUoj+FFuTEJh6/phbhvLLiu31VhzcxyiBP+Fgc= +github.com/MetaCubeX/sing-wireguard v0.0.0-20221109081324-ec09c458d37a/go.mod h1:8Gonxyo+mozu+jCshM+1krRnkNRW5RkrPPQSAJ8758s= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -103,12 +105,14 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= -github.com/sagernet/sing v0.0.0-20220929000216-9a83e35b7186 h1:ZDlgH6dTozS3ODaYq1GxCj+H8NvYESaex90iX72gadw= -github.com/sagernet/sing v0.0.0-20220929000216-9a83e35b7186/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4= +github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4 h1:LO7xMvMGhYmjQg2vjhTzsODyzs9/WLYu5Per+/8jIeo= +github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4= github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4= github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM= github.com/sagernet/sing-vmess v0.0.0-20220921140858-b6a1bdee672f h1:xyJ3Wbibcug4DxLi/FCHX2Td667SfieyZv645b8+eEE= github.com/sagernet/sing-vmess v0.0.0-20220921140858-b6a1bdee672f/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM= +github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c h1:qP3ZOHnjZalvqbjundbXiv/YrNlo3HOgrKc+S1QGs0U= +github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -138,8 +142,8 @@ go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66 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.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A= -golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 h1:RjggHMcaTVp0LOVZcW0bo8alwHrOaCrGUDgfWUHhnN4= golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -159,8 +163,8 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20221004154528-8021a29435af h1:wv66FM3rLZGPdxpYL+ApnDe2HzHcTFta3z5nsc13wI4= -golang.org/x/net v0.0.0-20221004154528-8021a29435af/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0 h1:cu5kTvlzcw1Q5S9f5ip1/cpiB4nXvw1XYzFPGgzLUOY= @@ -190,14 +194,14 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875 h1:AzgQNqF+FKwyQ5LbVrVqOcuuFB67N47F9+htZYH0wFM= -golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab h1:eHo2TTVBaAPw9lDGK2Gb9GyPMXT6g7O63W6sx3ylbzU= -golang.org/x/text v0.3.8-0.20220124021120-d1c84af989ab/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y= golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From b699fb046b7ed08d028d452ca9c218eb69f01e8a Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 9 Nov 2022 19:35:03 +0800 Subject: [PATCH 086/250] fix: wireguard's dns resolve --- adapter/outbound/wireguard.go | 27 +++++++++++++++------------ component/resolver/resolver.go | 34 +++++++++++++++++++++++++++------- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index fe26285e..0dd13515 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -43,7 +43,7 @@ type WireGuardOption struct { Name string `proxy:"name"` Server string `proxy:"server"` Port int `proxy:"port"` - Ip string `proxy:"ip"` + Ip string `proxy:"ip,omitempty"` Ipv6 string `proxy:"ipv6,omitempty"` PrivateKey string `proxy:"private-key"` PublicKey string `proxy:"public-key"` @@ -94,16 +94,15 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { peerAddr.Port = uint16(option.Port) outbound.bind = wireguard.NewClientBind(context.Background(), outbound.dialer, peerAddr, reserved) localPrefixes := make([]netip.Prefix, 0, 2) - if len(option.Ip) == 0 { - return nil, E.New("missing local address") - } - if !strings.Contains(option.Ip, "/") { - option.Ip = option.Ip + "/32" - } - if prefix, err := netip.ParsePrefix(option.Ip); err == nil { - localPrefixes = append(localPrefixes, prefix) - } else { - return nil, E.Cause(err, "ip address parse error") + if len(option.Ip) > 0 { + if !strings.Contains(option.Ip, "/") { + option.Ip = option.Ip + "/32" + } + if prefix, err := netip.ParsePrefix(option.Ip); err == nil { + localPrefixes = append(localPrefixes, prefix) + } else { + return nil, E.Cause(err, "ip address parse error") + } } if len(option.Ipv6) > 0 { if !strings.Contains(option.Ipv6, "/") { @@ -115,6 +114,9 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { return nil, E.Cause(err, "ipv6 address parse error") } } + if len(localPrefixes) == 0 { + return nil, E.New("missing local address") + } var privateKey, peerPublicKey, preSharedKey string { bytes, err := base64.StdEncoding.DecodeString(option.PrivateKey) @@ -202,7 +204,8 @@ func (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata, opts return nil, err } if !metadata.Resolved() { - addrs, err := resolver.ResolveAllIP(metadata.Host) + var addrs []netip.Addr + addrs, err = resolver.ResolveAllIP(metadata.Host) if err != nil { return nil, err } diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index af32cc94..51df094f 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -154,7 +154,15 @@ func ResolveAllIPv6WithResolver(host string, r Resolver) ([]netip.Addr, error) { return []netip.Addr{}, ErrIPNotFound } - return []netip.Addr{netip.AddrFrom16(*(*[16]byte)(ipAddrs[rand.Intn(len(ipAddrs))]))}, nil + addrs := make([]netip.Addr, 0, len(ipAddrs)) + for _, ipAddr := range ipAddrs { + addrs = append(addrs, nnip.IpToAddr(ipAddr)) + } + + rand.Shuffle(len(addrs), func(i, j int) { + addrs[i], addrs[j] = addrs[j], addrs[i] + }) + return addrs, nil } return []netip.Addr{}, ErrIPNotFound } @@ -188,12 +196,15 @@ func ResolveAllIPv4WithResolver(host string, r Resolver) ([]netip.Addr, error) { return []netip.Addr{}, ErrIPNotFound } - ip := ipAddrs[rand.Intn(len(ipAddrs))].To4() - if ip == nil { - return []netip.Addr{}, ErrIPVersion + addrs := make([]netip.Addr, 0, len(ipAddrs)) + for _, ipAddr := range ipAddrs { + addrs = append(addrs, nnip.IpToAddr(ipAddr)) } - return []netip.Addr{netip.AddrFrom4(*(*[4]byte)(ip))}, nil + rand.Shuffle(len(addrs), func(i, j int) { + addrs[i], addrs[j] = addrs[j], addrs[i] + }) + return addrs, nil } return []netip.Addr{}, ErrIPNotFound } @@ -219,12 +230,21 @@ func ResolveAllIPWithResolver(host string, r Resolver) ([]netip.Addr, error) { } if DefaultResolver == nil { - ipAddr, err := net.ResolveIPAddr("ip", host) + ipAddrs, err := net.DefaultResolver.LookupIP(context.Background(), "ip", host) if err != nil { return []netip.Addr{}, err + } else if len(ipAddrs) == 0 { + return []netip.Addr{}, ErrIPNotFound + } + addrs := make([]netip.Addr, 0, len(ipAddrs)) + for _, ipAddr := range ipAddrs { + addrs = append(addrs, nnip.IpToAddr(ipAddr)) } - return []netip.Addr{nnip.IpToAddr(ipAddr.IP)}, nil + rand.Shuffle(len(addrs), func(i, j int) { + addrs[i], addrs[j] = addrs[j], addrs[i] + }) + return addrs, nil } return []netip.Addr{}, ErrIPNotFound } From 4b4c3dc41e15a28f91b03ada2f4a2871ec200bee Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 9 Nov 2022 19:42:56 +0800 Subject: [PATCH 087/250] fix: small-case import name --- adapter/outbound/wireguard.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 0dd13515..a6808078 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -19,7 +19,7 @@ import ( C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/listener/sing" - wireguard "github.com/MetaCubeX/sing-wireguard" + wireguard "github.com/metacubex/sing-wireguard" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/debug" diff --git a/go.mod b/go.mod index 9a8994f1..199e3961 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/Dreamacro/clash go 1.19 require ( - github.com/MetaCubeX/sing-wireguard v0.0.0-20221109081324-ec09c458d37a github.com/cilium/ebpf v0.9.3 github.com/coreos/go-iptables v0.6.0 github.com/database64128/tfo-go v1.1.2 @@ -18,6 +17,7 @@ require ( github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c github.com/lucas-clemente/quic-go v0.29.1 github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 + github.com/metacubex/sing-wireguard v0.0.0-20221109114053-16c22adda03c github.com/miekg/dns v1.1.50 github.com/oschwald/geoip2-golang v1.8.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 diff --git a/go.sum b/go.sum index 48c5cecb..e8156fa3 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,6 @@ github.com/HyNetwork/quic-go v0.30.1-0.20221105180419-83715d7269a8 h1:FBo40lMrk1 github.com/HyNetwork/quic-go v0.30.1-0.20221105180419-83715d7269a8/go.mod h1:ssOrRsOmdxa768Wr78vnh2B8JozgLsMzG/g+0qEC7uk= github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc h1:B1vmR4cwDjJQ6YM8NkuXmt9vIYOdV/+qA+F3UAFE3YY= github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc/go.mod h1:1u3pjXA9HmH7kRiBJqM3C/zPxrxnCLd3svmqtub/RFU= -github.com/MetaCubeX/sing-wireguard v0.0.0-20221109081324-ec09c458d37a h1:PG0LSxUoj+FFuTEJh6/phbhvLLiu31VhzcxyiBP+Fgc= -github.com/MetaCubeX/sing-wireguard v0.0.0-20221109081324-ec09c458d37a/go.mod h1:8Gonxyo+mozu+jCshM+1krRnkNRW5RkrPPQSAJ8758s= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -85,6 +83,8 @@ github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcK github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= +github.com/metacubex/sing-wireguard v0.0.0-20221109114053-16c22adda03c h1:VHtXDny/TNOF7YDT9d9Qkr+x6K1O4cejXLlyPUXDeXQ= +github.com/metacubex/sing-wireguard v0.0.0-20221109114053-16c22adda03c/go.mod h1:fULJ451x1/XlpIhl+Oo+EPGKla9tFZaqT5dKLrZ+NvM= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI= From 93ada8989fd73759d5ea88741a1dbf4353bac611 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 9 Nov 2022 19:55:30 +0800 Subject: [PATCH 088/250] Update README.md --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index acdc0e69..1f5cb133 100644 --- a/README.md +++ b/README.md @@ -212,6 +212,21 @@ proxies: grpc-service-name: grpcname ``` + +Support outbound transport protocol `Wireguard` +```yaml +proxies: + - name: "wg" + type: wireguard + server: 162.159.192.1 + port: 2480 + ip: 172.16.0.2 + ipv6: fd01:5ca1:ab1e:80fa:ab85:6eea:213f:f4a5 + private-key: eCtXsJZ27+4PbhDkHnB923tkUn2Gj59wZw5wFA75MnU= + public-key: Cr8hWlKvtDt7nrvf+f0brNQQzabAqrjfBvas9pmowjo= + udp: true +``` + ### IPTABLES configuration Work on Linux OS who's supported `iptables` From 7c8d8f56e18518e793e448c0c9c5a28cc37e0e29 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 9 Nov 2022 22:55:32 +0800 Subject: [PATCH 089/250] Update docs/config.yaml --- docs/config.yaml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/config.yaml b/docs/config.yaml index 6c8c7c26..a079a9d1 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -42,7 +42,7 @@ tun: enable: false stack: system # gvisor / lwip dns-hijack: - - 198.18.0.2:53 # 需要劫持的 DNS + - 0.0.0.0:53 # 需要劫持的 DNS # auto-detect-interface: true # 自动识别出口网卡 # auto-route: true # 配置路由表 # mtu: 9000 # 最大传输单元 @@ -441,6 +441,16 @@ proxies: #disable_mtu_discovery: false # fingerprint: xxxx + - name: "wg" + type: wireguard + server: 162.159.192.1 + port: 2480 + ip: 172.16.0.2 + ipv6: fd01:5ca1:ab1e:80fa:ab85:6eea:213f:f4a5 + private-key: eCtXsJZ27+4PbhDkHnB923tkUn2Gj59wZw5wFA75MnU= + public-key: Cr8hWlKvtDt7nrvf+f0brNQQzabAqrjfBvas9pmowjo= + udp: true + # ShadowsocksR # The supported ciphers (encryption methods): all stream ciphers in ss # The supported obfses: From 64552fbd005a28f4d0ad38dcdcbd9c98ab1ad00a Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 10 Nov 2022 21:08:06 +0800 Subject: [PATCH 090/250] fix: when host's ip in fakeip's range, don't send to remote server --- config/config.go | 15 +++++++-------- tunnel/tunnel.go | 18 +++++++++++++++--- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/config/config.go b/config/config.go index 57bf55e8..572031d3 100644 --- a/config/config.go +++ b/config/config.go @@ -495,7 +495,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } config.DNS = dnsCfg - err = parseTun(rawCfg.Tun, config.General, dnsCfg) + err = parseTun(rawCfg.Tun, config.General) if err != nil { return nil, err } @@ -1053,8 +1053,9 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[netip.Addr], rules []C.R } } + fakeIPRange, err := netip.ParsePrefix(cfg.FakeIPRange) + T.SetFakeIPRange(fakeIPRange) if cfg.EnhancedMode == C.DNSFakeIP { - ipnet, err := netip.ParsePrefix(cfg.FakeIPRange) if err != nil { return nil, err } @@ -1081,7 +1082,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[netip.Addr], rules []C.R } pool, err := fakeip.New(fakeip.Options{ - IPNet: &ipnet, + IPNet: &fakeIPRange, Size: 1000, Host: host, Persistence: rawCfg.Profile.StoreFakeIP, @@ -1124,7 +1125,7 @@ func parseAuthentication(rawRecords []string) []auth.AuthUser { return users } -func parseTun(rawTun RawTun, general *General, dnsCfg *DNS) error { +func parseTun(rawTun RawTun, general *General) error { var dnsHijack []netip.AddrPort for _, d := range rawTun.DNSHijack { @@ -1140,10 +1141,8 @@ func parseTun(rawTun RawTun, general *General, dnsCfg *DNS) error { dnsHijack = append(dnsHijack, addrPort) } - var tunAddressPrefix netip.Prefix - if dnsCfg.FakeIPRange != nil { - tunAddressPrefix = *dnsCfg.FakeIPRange.IPNet() - } else { + tunAddressPrefix := T.FakeIPRange() + if !tunAddressPrefix.IsValid() { tunAddressPrefix = netip.MustParsePrefix("198.18.0.1/16") } tunAddressPrefix = netip.PrefixFrom(tunAddressPrefix.Addr(), 30) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 760fa103..ae09499c 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -41,8 +41,18 @@ var ( udpTimeout = 60 * time.Second alwaysFindProcess = false + + fakeIPRange netip.Prefix ) +func SetFakeIPRange(p netip.Prefix) { + fakeIPRange = p +} + +func FakeIPRange() netip.Prefix { + return fakeIPRange +} + func SetSniffing(b bool) { if sniffer.Dispatcher.Enable() { configMux.Lock() @@ -334,9 +344,11 @@ func handleTCPConn(connCtx C.ConnContext) { dialMetadata := metadata if len(metadata.Host) > 0 { if node := resolver.DefaultHosts.Search(metadata.Host); node != nil { - dialMetadata.DstIP = node.Data() - dialMetadata.DNSMode = C.DNSHosts - dialMetadata = dialMetadata.Pure() + if dstIp := node.Data(); !FakeIPRange().Contains(dstIp) { + dialMetadata.DstIP = dstIp + dialMetadata.DNSMode = C.DNSHosts + dialMetadata = dialMetadata.Pure() + } } } From 1a4b00c70ef0262dc9f27f494b943fb80a2f68a3 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 10 Nov 2022 21:23:52 +0800 Subject: [PATCH 091/250] fix: update sing-vmess --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 199e3961..03cd4821 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4 github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 github.com/sagernet/sing-tun v0.0.0-20221012082254-488c3b75f6fd - github.com/sagernet/sing-vmess v0.0.0-20220921140858-b6a1bdee672f + github.com/sagernet/sing-vmess v0.0.0-20221109021549-b446d5bdddf0 github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.0 diff --git a/go.sum b/go.sum index e8156fa3..9a5d0133 100644 --- a/go.sum +++ b/go.sum @@ -109,8 +109,8 @@ github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4 h1:LO7xMvMGhYmjQg2vj github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4= github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4= github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM= -github.com/sagernet/sing-vmess v0.0.0-20220921140858-b6a1bdee672f h1:xyJ3Wbibcug4DxLi/FCHX2Td667SfieyZv645b8+eEE= -github.com/sagernet/sing-vmess v0.0.0-20220921140858-b6a1bdee672f/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM= +github.com/sagernet/sing-vmess v0.0.0-20221109021549-b446d5bdddf0 h1:z3kuD3hPNdEq7/wVy5lwE21f+8ZTazBtR81qswxJoCc= +github.com/sagernet/sing-vmess v0.0.0-20221109021549-b446d5bdddf0/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM= github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c h1:qP3ZOHnjZalvqbjundbXiv/YrNlo3HOgrKc+S1QGs0U= github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= From 698d8ca7010ddb8f07a102fdbfdf94f50e1c2b0d Mon Sep 17 00:00:00 2001 From: cubemaze <117334465+cubemaze@users.noreply.github.com> Date: Fri, 11 Nov 2022 04:05:13 +0800 Subject: [PATCH 092/250] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1f5cb133..ce16182d 100644 --- a/README.md +++ b/README.md @@ -301,6 +301,7 @@ the [GitHub Wiki](https://github.com/Dreamacro/clash/wiki/use-clash-as-a-library ## Credits * [Dreamacro/clash](https://github.com/Dreamacro/clash) +* [SagerNet/sing-box](https://github.com/SagerNet/sing-box) * [riobard/go-shadowsocks2](https://github.com/riobard/go-shadowsocks2) * [v2ray/v2ray-core](https://github.com/v2ray/v2ray-core) * [WireGuard/wireguard-go](https://github.com/WireGuard/wireguard-go) From 6dadc2357aed2115dac80bf34d19d15e4f7d7a51 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 11 Nov 2022 09:19:28 +0800 Subject: [PATCH 093/250] chore: remove AddrType on Metadata --- adapter/adapter.go | 7 +++---- adapter/inbound/socket.go | 5 ----- adapter/inbound/util.go | 19 +++++------------ adapter/outbound/util.go | 5 +++-- adapter/outbound/vless.go | 13 ++++++------ adapter/outboundgroup/util.go | 27 +++++++------------------ component/sniffer/dispatcher.go | 1 - constant/metadata.go | 23 ++++++++++++--------- dns/util.go | 14 ++++--------- rules/common/domain.go | 3 --- rules/common/domain_keyword.go | 3 --- rules/common/domain_suffix.go | 3 --- rules/common/geosite.go | 4 ---- rules/logic/logic_test.go | 7 +++---- test/clash_test.go | 36 ++++++++++++++------------------- tunnel/tunnel.go | 6 ------ 16 files changed, 60 insertions(+), 116 deletions(-) diff --git a/adapter/adapter.go b/adapter/adapter.go index 2b3c7e6c..aa860ed4 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -198,10 +198,9 @@ func urlToMetadata(rawURL string) (addr C.Metadata, err error) { } addr = C.Metadata{ - AddrType: C.AtypDomainName, - Host: u.Hostname(), - DstIP: netip.Addr{}, - DstPort: port, + Host: u.Hostname(), + DstIP: netip.Addr{}, + DstPort: port, } return } diff --git a/adapter/inbound/socket.go b/adapter/inbound/socket.go index 28608be9..7fe00dbf 100644 --- a/adapter/inbound/socket.go +++ b/adapter/inbound/socket.go @@ -32,17 +32,12 @@ func NewInner(conn net.Conn, dst string, host string) *context.ConnContext { metadata.Type = C.INNER metadata.DNSMode = C.DNSMapping metadata.Host = host - metadata.AddrType = C.AtypDomainName metadata.Process = C.ClashName if h, port, err := net.SplitHostPort(dst); err == nil { metadata.DstPort = port if host == "" { if ip, err := netip.ParseAddr(h); err == nil { metadata.DstIP = ip - metadata.AddrType = C.AtypIPv4 - if ip.Is6() { - metadata.AddrType = C.AtypIPv6 - } } } } diff --git a/adapter/inbound/util.go b/adapter/inbound/util.go index 9a024529..cddbcf1f 100644 --- a/adapter/inbound/util.go +++ b/adapter/inbound/util.go @@ -13,9 +13,7 @@ import ( ) func parseSocksAddr(target socks5.Addr) *C.Metadata { - metadata := &C.Metadata{ - AddrType: int(target[0]), - } + metadata := &C.Metadata{} switch target[0] { case socks5.AtypDomainName: @@ -45,21 +43,14 @@ func parseHTTPAddr(request *http.Request) *C.Metadata { host = strings.TrimRight(host, ".") metadata := &C.Metadata{ - NetWork: C.TCP, - AddrType: C.AtypDomainName, - Host: host, - DstIP: netip.Addr{}, - DstPort: port, + NetWork: C.TCP, + Host: host, + DstIP: netip.Addr{}, + DstPort: port, } ip, err := netip.ParseAddr(host) if err == nil { - switch { - case ip.Is6(): - metadata.AddrType = C.AtypIPv6 - default: - metadata.AddrType = C.AtypIPv4 - } metadata.DstIP = ip } diff --git a/adapter/outbound/util.go b/adapter/outbound/util.go index d5219131..06c81868 100644 --- a/adapter/outbound/util.go +++ b/adapter/outbound/util.go @@ -44,10 +44,11 @@ func getClientXSessionCache() xtls.ClientSessionCache { func serializesSocksAddr(metadata *C.Metadata) []byte { var buf [][]byte - aType := uint8(metadata.AddrType) + addrType := metadata.AddrType() + aType := uint8(addrType) p, _ := strconv.ParseUint(metadata.DstPort, 10, 16) port := []byte{uint8(p >> 8), uint8(p & 0xff)} - switch metadata.AddrType { + switch addrType { case socks5.AtypDomainName: lenM := uint8(len(metadata.Host)) host := []byte(metadata.Host) diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index 4811de0f..5eb081c1 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -6,18 +6,19 @@ import ( "encoding/binary" "errors" "fmt" - "github.com/Dreamacro/clash/common/convert" - tlsC "github.com/Dreamacro/clash/component/tls" "io" "net" "net/http" "strconv" "sync" + "github.com/Dreamacro/clash/common/convert" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/resolver" + tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/transport/gun" + "github.com/Dreamacro/clash/transport/socks5" "github.com/Dreamacro/clash/transport/vless" "github.com/Dreamacro/clash/transport/vmess" ) @@ -280,16 +281,16 @@ func (v *Vless) SupportUOT() bool { func parseVlessAddr(metadata *C.Metadata) *vless.DstAddr { var addrType byte var addr []byte - switch metadata.AddrType { - case C.AtypIPv4: + switch metadata.AddrType() { + case socks5.AtypIPv4: addrType = vless.AtypIPv4 addr = make([]byte, net.IPv4len) copy(addr[:], metadata.DstIP.AsSlice()) - case C.AtypIPv6: + case socks5.AtypIPv6: addrType = vless.AtypIPv6 addr = make([]byte, net.IPv6len) copy(addr[:], metadata.DstIP.AsSlice()) - case C.AtypDomainName: + case socks5.AtypDomainName: addrType = vless.AtypDomainName addr = make([]byte, len(metadata.Host)+1) addr[0] = byte(len(metadata.Host)) diff --git a/adapter/outboundgroup/util.go b/adapter/outboundgroup/util.go index e1700d94..3c769b2f 100644 --- a/adapter/outboundgroup/util.go +++ b/adapter/outboundgroup/util.go @@ -16,32 +16,19 @@ func addrToMetadata(rawAddress string) (addr *C.Metadata, err error) { return } - ip, err := netip.ParseAddr(host) - if err != nil { + if ip, err := netip.ParseAddr(host); err != nil { addr = &C.Metadata{ - AddrType: C.AtypDomainName, - Host: host, - DstIP: netip.Addr{}, - DstPort: port, + Host: host, + DstPort: port, } - err = nil - return - } else if ip.Is4() { + } else { addr = &C.Metadata{ - AddrType: C.AtypIPv4, - Host: "", - DstIP: ip, - DstPort: port, + Host: "", + DstIP: ip, + DstPort: port, } - return } - addr = &C.Metadata{ - AddrType: C.AtypIPv6, - Host: "", - DstIP: ip, - DstPort: port, - } return } diff --git a/component/sniffer/dispatcher.go b/component/sniffer/dispatcher.go index 3bd81ac8..ef586d45 100644 --- a/component/sniffer/dispatcher.go +++ b/component/sniffer/dispatcher.go @@ -112,7 +112,6 @@ func (sd *SnifferDispatcher) replaceDomain(metadata *C.Metadata, host string) { metadata.Host, host) } - metadata.AddrType = C.AtypDomainName metadata.Host = host metadata.DNSMode = C.DNSNormal } diff --git a/constant/metadata.go b/constant/metadata.go index f248cd21..2d52f2bf 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -6,14 +6,12 @@ import ( "net" "net/netip" "strconv" + + "github.com/Dreamacro/clash/transport/socks5" ) // Socks addr type const ( - AtypIPv4 = 1 - AtypDomainName = 3 - AtypIPv6 = 4 - TCP NetWork = iota UDP ALLNet @@ -105,7 +103,6 @@ type Metadata struct { DstIP netip.Addr `json:"destinationIP"` SrcPort string `json:"sourcePort"` DstPort string `json:"destinationPort"` - AddrType int `json:"-"` Host string `json:"host"` DNSMode DNSMode `json:"dnsMode"` Uid *int32 `json:"uid"` @@ -138,6 +135,17 @@ func (m *Metadata) SourceDetail() string { } } +func (m *Metadata) AddrType() int { + switch true { + case m.Host != "" || !m.DstIP.IsValid(): + return socks5.AtypDomainName + case m.DstIP.Is4(): + return socks5.AtypIPv4 + default: + return socks5.AtypIPv6 + } +} + func (m *Metadata) Resolved() bool { return m.DstIP.IsValid() } @@ -148,11 +156,6 @@ func (m *Metadata) Pure() *Metadata { if (m.DNSMode == DNSMapping || m.DNSMode == DNSHosts) && m.DstIP.IsValid() { copyM := *m copyM.Host = "" - if copyM.DstIP.Is4() { - copyM.AddrType = AtypIPv4 - } else { - copyM.AddrType = AtypIPv6 - } return ©M } diff --git a/dns/util.go b/dns/util.go index 50d9decd..2958a9ed 100644 --- a/dns/util.go +++ b/dns/util.go @@ -156,17 +156,11 @@ func dialContextExtra(ctx context.Context, adapterName string, network string, d networkType = C.UDP } - addrType := C.AtypIPv4 - if dstIP.Is6() { - addrType = C.AtypIPv6 - } - metadata := &C.Metadata{ - NetWork: networkType, - AddrType: addrType, - Host: "", - DstIP: dstIP, - DstPort: port, + NetWork: networkType, + Host: "", + DstIP: dstIP, + DstPort: port, } adapter, ok := tunnel.Proxies()[adapterName] diff --git a/rules/common/domain.go b/rules/common/domain.go index 2f0e4f2f..1dc6b250 100644 --- a/rules/common/domain.go +++ b/rules/common/domain.go @@ -19,9 +19,6 @@ func (d *Domain) RuleType() C.RuleType { } func (d *Domain) Match(metadata *C.Metadata) (bool, string) { - if metadata.AddrType != C.AtypDomainName { - return false, "" - } return metadata.Host == d.domain, d.adapter } diff --git a/rules/common/domain_keyword.go b/rules/common/domain_keyword.go index 58257544..4fff673e 100644 --- a/rules/common/domain_keyword.go +++ b/rules/common/domain_keyword.go @@ -19,9 +19,6 @@ func (dk *DomainKeyword) RuleType() C.RuleType { } func (dk *DomainKeyword) Match(metadata *C.Metadata) (bool, string) { - if metadata.AddrType != C.AtypDomainName { - return false, "" - } domain := metadata.Host return strings.Contains(domain, dk.keyword), dk.adapter } diff --git a/rules/common/domain_suffix.go b/rules/common/domain_suffix.go index c9016744..1e48a236 100644 --- a/rules/common/domain_suffix.go +++ b/rules/common/domain_suffix.go @@ -19,9 +19,6 @@ func (ds *DomainSuffix) RuleType() C.RuleType { } func (ds *DomainSuffix) Match(metadata *C.Metadata) (bool, string) { - if metadata.AddrType != C.AtypDomainName { - return false, "" - } domain := metadata.Host return strings.HasSuffix(domain, "."+ds.suffix) || domain == ds.suffix, ds.adapter } diff --git a/rules/common/geosite.go b/rules/common/geosite.go index 9897f349..ad3cbae5 100644 --- a/rules/common/geosite.go +++ b/rules/common/geosite.go @@ -24,10 +24,6 @@ func (gs *GEOSITE) RuleType() C.RuleType { } func (gs *GEOSITE) Match(metadata *C.Metadata) (bool, string) { - if metadata.AddrType != C.AtypDomainName { - return false, "" - } - domain := metadata.Host return gs.matcher.ApplyDomain(domain), gs.adapter } diff --git a/rules/logic/logic_test.go b/rules/logic/logic_test.go index 4ec72cc4..0410873e 100644 --- a/rules/logic/logic_test.go +++ b/rules/logic/logic_test.go @@ -73,10 +73,9 @@ func TestAND(t *testing.T) { assert.Equal(t, "DIRECT", and.adapter) assert.Equal(t, false, and.ShouldResolveIP()) m, _ := and.Match(&C.Metadata{ - Host: "baidu.com", - AddrType: C.AtypDomainName, - NetWork: C.TCP, - DstPort: "20000", + Host: "baidu.com", + NetWork: C.TCP, + DstPort: "20000", }) assert.Equal(t, true, m) diff --git a/test/clash_test.go b/test/clash_test.go index 2cc5bd9a..e6112e21 100644 --- a/test/clash_test.go +++ b/test/clash_test.go @@ -555,9 +555,8 @@ func testPacketConnTimeout(t *testing.T, pc net.PacketConn) error { func testSuit(t *testing.T, proxy C.ProxyAdapter) { assert.NoError(t, testPingPongWithConn(t, func() net.Conn { conn, err := proxy.DialContext(context.Background(), &C.Metadata{ - Host: localIP.String(), - DstPort: "10001", - AddrType: socks5.AtypDomainName, + Host: localIP.String(), + DstPort: "10001", }) require.NoError(t, err) return conn @@ -565,9 +564,8 @@ func testSuit(t *testing.T, proxy C.ProxyAdapter) { assert.NoError(t, testLargeDataWithConn(t, func() net.Conn { conn, err := proxy.DialContext(context.Background(), &C.Metadata{ - Host: localIP.String(), - DstPort: "10001", - AddrType: socks5.AtypDomainName, + Host: localIP.String(), + DstPort: "10001", }) require.NoError(t, err) return conn @@ -578,10 +576,9 @@ func testSuit(t *testing.T, proxy C.ProxyAdapter) { } pc, err := proxy.ListenPacketContext(context.Background(), &C.Metadata{ - NetWork: C.UDP, - DstIP: localIP, - DstPort: "10001", - AddrType: socks5.AtypIPv4, + NetWork: C.UDP, + DstIP: localIP, + DstPort: "10001", }) require.NoError(t, err) defer pc.Close() @@ -589,10 +586,9 @@ func testSuit(t *testing.T, proxy C.ProxyAdapter) { assert.NoError(t, testPingPongWithPacketConn(t, pc)) pc, err = proxy.ListenPacketContext(context.Background(), &C.Metadata{ - NetWork: C.UDP, - DstIP: localIP, - DstPort: "10001", - AddrType: socks5.AtypIPv4, + NetWork: C.UDP, + DstIP: localIP, + DstPort: "10001", }) require.NoError(t, err) defer pc.Close() @@ -600,10 +596,9 @@ func testSuit(t *testing.T, proxy C.ProxyAdapter) { assert.NoError(t, testLargeDataWithPacketConn(t, pc)) pc, err = proxy.ListenPacketContext(context.Background(), &C.Metadata{ - NetWork: C.UDP, - DstIP: localIP, - DstPort: "10001", - AddrType: socks5.AtypIPv4, + NetWork: C.UDP, + DstIP: localIP, + DstPort: "10001", }) require.NoError(t, err) defer pc.Close() @@ -639,9 +634,8 @@ func benchmarkProxy(b *testing.B, proxy C.ProxyAdapter) { }() conn, err := proxy.DialContext(context.Background(), &C.Metadata{ - Host: localIP.String(), - DstPort: "10001", - AddrType: socks5.AtypDomainName, + Host: localIP.String(), + DstPort: "10001", }) require.NoError(b, err) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index ae09499c..46b603a3 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -169,11 +169,6 @@ func preHandleMetadata(metadata *C.Metadata) error { if ip, err := netip.ParseAddr(metadata.Host); err == nil { metadata.DstIP = ip metadata.Host = "" - if ip.Is4() { - metadata.AddrType = C.AtypIPv4 - } else { - metadata.AddrType = C.AtypIPv6 - } } // preprocess enhanced-mode metadata @@ -181,7 +176,6 @@ func preHandleMetadata(metadata *C.Metadata) error { host, exist := resolver.FindHostByIP(metadata.DstIP) if exist { metadata.Host = host - metadata.AddrType = C.AtypDomainName metadata.DNSMode = C.DNSMapping if resolver.FakeIPEnabled() { metadata.DstIP = netip.Addr{} From 3eacce9a668d1fd738fee366d0a2ce2224b57156 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 11 Nov 2022 20:56:08 +0800 Subject: [PATCH 094/250] chore: add vmess, shadowsocks, tcptun and udptun listener --- config/config.go | 46 +++--- constant/metadata.go | 12 ++ hub/executor/executor.go | 24 ++- hub/route/configs.go | 44 ++++-- listener/listener.go | 219 +++++++++++++++++++++++++--- listener/sing_shadowsocks/server.go | 173 ++++++++++++++++++++++ listener/sing_vmess/server.go | 126 ++++++++++++++++ listener/tunnel/tcp.go | 68 +++++++++ listener/tunnel/udp.go | 79 ++++++++++ listener/tunnel/utils.go | 63 ++++++++ 10 files changed, 796 insertions(+), 58 deletions(-) create mode 100644 listener/sing_shadowsocks/server.go create mode 100644 listener/sing_vmess/server.go create mode 100644 listener/tunnel/tcp.go create mode 100644 listener/tunnel/udp.go create mode 100644 listener/tunnel/utils.go diff --git a/config/config.go b/config/config.go index 572031d3..f04f13de 100644 --- a/config/config.go +++ b/config/config.go @@ -60,15 +60,19 @@ type General struct { // Inbound config type Inbound struct { - Port int `json:"port"` - SocksPort int `json:"socks-port"` - RedirPort int `json:"redir-port"` - TProxyPort int `json:"tproxy-port"` - MixedPort int `json:"mixed-port"` - Authentication []string `json:"authentication"` - AllowLan bool `json:"allow-lan"` - BindAddress string `json:"bind-address"` - InboundTfo bool `json:"inbound-tfo"` + Port int `json:"port"` + SocksPort int `json:"socks-port"` + RedirPort int `json:"redir-port"` + TProxyPort int `json:"tproxy-port"` + MixedPort int `json:"mixed-port"` + ShadowSocksConfig string `json:"ss-config"` + VmessConfig string `json:"vmess-config"` + TcpTunConfig string `json:"tcptun-config"` + UdpTunConfig string `json:"udptun-config"` + Authentication []string `json:"authentication"` + AllowLan bool `json:"allow-lan"` + BindAddress string `json:"bind-address"` + InboundTfo bool `json:"inbound-tfo"` } // Controller config @@ -284,6 +288,10 @@ type RawConfig struct { RedirPort int `yaml:"redir-port"` TProxyPort int `yaml:"tproxy-port"` MixedPort int `yaml:"mixed-port"` + ShadowSocksConfig string `yaml:"ss-config"` + VmessConfig string `yaml:"vmess-config"` + TcpTunConfig string `yaml:"tcptun-config"` + UdpTunConfig string `yaml:"udptun-config"` InboundTfo bool `yaml:"inbound-tfo"` Authentication []string `yaml:"authentication"` AllowLan bool `yaml:"allow-lan"` @@ -526,14 +534,18 @@ func parseGeneral(cfg *RawConfig) (*General, error) { cfg.Tun.RedirectToTun = cfg.EBpf.RedirectToTun return &General{ Inbound: Inbound{ - Port: cfg.Port, - SocksPort: cfg.SocksPort, - RedirPort: cfg.RedirPort, - TProxyPort: cfg.TProxyPort, - MixedPort: cfg.MixedPort, - AllowLan: cfg.AllowLan, - BindAddress: cfg.BindAddress, - InboundTfo: cfg.InboundTfo, + Port: cfg.Port, + SocksPort: cfg.SocksPort, + RedirPort: cfg.RedirPort, + TProxyPort: cfg.TProxyPort, + MixedPort: cfg.MixedPort, + ShadowSocksConfig: cfg.ShadowSocksConfig, + VmessConfig: cfg.VmessConfig, + TcpTunConfig: cfg.TcpTunConfig, + UdpTunConfig: cfg.UdpTunConfig, + AllowLan: cfg.AllowLan, + BindAddress: cfg.BindAddress, + InboundTfo: cfg.InboundTfo, }, Controller: Controller{ ExternalController: cfg.ExternalController, diff --git a/constant/metadata.go b/constant/metadata.go index 2d52f2bf..61a98be3 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -20,8 +20,12 @@ const ( HTTPS SOCKS4 SOCKS5 + SHADOWSOCKS + VMESS REDIR TPROXY + TCPTUN + UDPTUN TUN INNER ) @@ -53,10 +57,18 @@ func (t Type) String() string { return "Socks4" case SOCKS5: return "Socks5" + case SHADOWSOCKS: + return "ShadowSocks" + case VMESS: + return "Vmess" case REDIR: return "Redir" case TPROXY: return "TProxy" + case TCPTUN: + return "TcpTun" + case UDPTUN: + return "UdpTun" case TUN: return "Tun" case INNER: diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 760cb52f..fad07136 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -105,14 +105,18 @@ func GetGeneral() *config.General { general := &config.General{ Inbound: config.Inbound{ - Port: ports.Port, - SocksPort: ports.SocksPort, - RedirPort: ports.RedirPort, - TProxyPort: ports.TProxyPort, - MixedPort: ports.MixedPort, - Authentication: authenticator, - AllowLan: P.AllowLan(), - BindAddress: P.BindAddress(), + Port: ports.Port, + SocksPort: ports.SocksPort, + RedirPort: ports.RedirPort, + TProxyPort: ports.TProxyPort, + MixedPort: ports.MixedPort, + ShadowSocksConfig: ports.ShadowSocksConfig, + VmessConfig: ports.VmessConfig, + TcpTunConfig: ports.TcpTunConfig, + UdpTunConfig: ports.UdpTunConfig, + Authentication: authenticator, + AllowLan: P.AllowLan(), + BindAddress: P.BindAddress(), }, Mode: tunnel.Mode(), LogLevel: log.Level(), @@ -342,6 +346,10 @@ func updateGeneral(general *config.General, force bool) { P.ReCreateAutoRedir(general.EBpf.AutoRedir, tcpIn, udpIn) P.ReCreateTProxy(general.TProxyPort, tcpIn, udpIn) P.ReCreateMixed(general.MixedPort, tcpIn, udpIn) + P.ReCreateShadowSocks(general.ShadowSocksConfig, tcpIn, udpIn) + P.ReCreateVmess(general.VmessConfig, tcpIn, udpIn) + P.ReCreateTcpTun(general.TcpTunConfig, tcpIn, udpIn) + P.ReCreateUdpTun(general.UdpTunConfig, tcpIn, udpIn) } func updateUsers(users []auth.AuthUser) { diff --git a/hub/route/configs.go b/hub/route/configs.go index 78c631ea..1e08dbda 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -35,20 +35,24 @@ func configRouter() http.Handler { } type configSchema struct { - Port *int `json:"port"` - SocksPort *int `json:"socks-port"` - RedirPort *int `json:"redir-port"` - TProxyPort *int `json:"tproxy-port"` - MixedPort *int `json:"mixed-port"` - Tun *tunSchema `json:"tun"` - AllowLan *bool `json:"allow-lan"` - BindAddress *string `json:"bind-address"` - Mode *tunnel.TunnelMode `json:"mode"` - LogLevel *log.LogLevel `json:"log-level"` - IPv6 *bool `json:"ipv6"` - Sniffing *bool `json:"sniffing"` - TcpConcurrent *bool `json:"tcp-concurrent"` - InterfaceName *string `json:"interface-name"` + Port *int `json:"port"` + SocksPort *int `json:"socks-port"` + RedirPort *int `json:"redir-port"` + TProxyPort *int `json:"tproxy-port"` + MixedPort *int `json:"mixed-port"` + Tun *tunSchema `json:"tun"` + ShadowSocksConfig *string `json:"ss-config"` + VmessConfig *string `json:"vmess-config"` + TcptunConfig *string `json:"tcptun-config"` + UdptunConfig *string `json:"udptun-config"` + AllowLan *bool `json:"allow-lan"` + BindAddress *string `json:"bind-address"` + Mode *tunnel.TunnelMode `json:"mode"` + LogLevel *log.LogLevel `json:"log-level"` + IPv6 *bool `json:"ipv6"` + Sniffing *bool `json:"sniffing"` + TcpConcurrent *bool `json:"tcp-concurrent"` + InterfaceName *string `json:"interface-name"` } type tunSchema struct { @@ -90,6 +94,14 @@ func pointerOrDefault(p *int, def int) int { return def } +func pointerOrDefaultString(p *string, def string) string { + if p != nil { + return *p + } + + return def +} + func pointerOrDefaultTun(p *tunSchema, def config.Tun) config.Tun { if p != nil { def.Enable = p.Enable @@ -187,6 +199,10 @@ func patchConfigs(w http.ResponseWriter, r *http.Request) { P.ReCreateTProxy(pointerOrDefault(general.TProxyPort, ports.TProxyPort), tcpIn, udpIn) P.ReCreateMixed(pointerOrDefault(general.MixedPort, ports.MixedPort), tcpIn, udpIn) P.ReCreateTun(pointerOrDefaultTun(general.Tun, P.LastTunConf), tcpIn, udpIn) + P.ReCreateShadowSocks(pointerOrDefaultString(general.ShadowSocksConfig, ports.ShadowSocksConfig), tcpIn, udpIn) + P.ReCreateVmess(pointerOrDefaultString(general.VmessConfig, ports.VmessConfig), tcpIn, udpIn) + P.ReCreateTcpTun(pointerOrDefaultString(general.TcptunConfig, ports.TcpTunConfig), tcpIn, udpIn) + P.ReCreateUdpTun(pointerOrDefaultString(general.UdptunConfig, ports.UdpTunConfig), tcpIn, udpIn) if general.Mode != nil { tunnel.SetMode(*general.Mode) diff --git a/listener/listener.go b/listener/listener.go index e608cb1e..c9e95226 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -2,7 +2,6 @@ package proxy import ( "fmt" - "github.com/Dreamacro/clash/listener/sing_tun" "golang.org/x/exp/slices" "net" "sort" @@ -18,8 +17,12 @@ import ( "github.com/Dreamacro/clash/listener/inner" "github.com/Dreamacro/clash/listener/mixed" "github.com/Dreamacro/clash/listener/redir" + "github.com/Dreamacro/clash/listener/sing_shadowsocks" + "github.com/Dreamacro/clash/listener/sing_tun" + "github.com/Dreamacro/clash/listener/sing_vmess" "github.com/Dreamacro/clash/listener/socks" "github.com/Dreamacro/clash/listener/tproxy" + "github.com/Dreamacro/clash/listener/tunnel" "github.com/Dreamacro/clash/log" ) @@ -28,19 +31,23 @@ var ( bindAddress = "*" inboundTfo = false - socksListener *socks.Listener - socksUDPListener *socks.UDPListener - httpListener *http.Listener - redirListener *redir.Listener - redirUDPListener *tproxy.UDPListener - tproxyListener *tproxy.Listener - tproxyUDPListener *tproxy.UDPListener - mixedListener *mixed.Listener - mixedUDPLister *socks.UDPListener - tunLister *sing_tun.Listener - autoRedirListener *autoredir.Listener - autoRedirProgram *ebpf.TcEBpfProgram - tcProgram *ebpf.TcEBpfProgram + socksListener *socks.Listener + socksUDPListener *socks.UDPListener + httpListener *http.Listener + redirListener *redir.Listener + redirUDPListener *tproxy.UDPListener + tproxyListener *tproxy.Listener + tproxyUDPListener *tproxy.UDPListener + mixedListener *mixed.Listener + mixedUDPLister *socks.UDPListener + tunLister *sing_tun.Listener + shadowSocksListener *sing_shadowsocks.Listener + vmessListener *sing_vmess.Listener + tcpTunListener *tunnel.Listener + udpTunListener *tunnel.UdpListener + autoRedirListener *autoredir.Listener + autoRedirProgram *ebpf.TcEBpfProgram + tcProgram *ebpf.TcEBpfProgram // lock for recreate function socksMux sync.Mutex @@ -49,6 +56,10 @@ var ( tproxyMux sync.Mutex mixedMux sync.Mutex tunMux sync.Mutex + ssMux sync.Mutex + vmessMux sync.Mutex + tcpTunMux sync.Mutex + udpTunMux sync.Mutex autoRedirMux sync.Mutex tcMux sync.Mutex @@ -56,11 +67,15 @@ var ( ) type Ports struct { - Port int `json:"port"` - SocksPort int `json:"socks-port"` - RedirPort int `json:"redir-port"` - TProxyPort int `json:"tproxy-port"` - MixedPort int `json:"mixed-port"` + Port int `json:"port"` + SocksPort int `json:"socks-port"` + RedirPort int `json:"redir-port"` + TProxyPort int `json:"tproxy-port"` + MixedPort int `json:"mixed-port"` + ShadowSocksConfig string `json:"ss-config"` + VmessConfig string `json:"vmess-config"` + TcpTunConfig string `json:"tcptun-config"` + UdpTunConfig string `json:"udptun-config"` } func GetTunConf() config.Tun { @@ -235,6 +250,156 @@ func ReCreateRedir(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P log.Infoln("Redirect proxy listening at: %s", redirListener.Address()) } +func ReCreateShadowSocks(shadowSocksConfig string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { + ssMux.Lock() + defer ssMux.Unlock() + + var err error + defer func() { + if err != nil { + log.Errorln("Start ShadowSocks server error: %s", err.Error()) + } + }() + + shouldIgnore := false + + if shadowSocksListener != nil { + if shadowSocksListener.Config() != shadowSocksConfig { + shadowSocksListener.Close() + shadowSocksListener = nil + } else { + shouldIgnore = true + } + } + + if shouldIgnore { + return + } + + if len(shadowSocksConfig) == 0 { + return + } + + listener, err := sing_shadowsocks.New(shadowSocksConfig, tcpIn, udpIn) + if err != nil { + return + } + + shadowSocksListener = listener + + return +} + +func ReCreateVmess(vmessConfig string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { + vmessMux.Lock() + defer vmessMux.Unlock() + + var err error + defer func() { + if err != nil { + log.Errorln("Start Vmess server error: %s", err.Error()) + } + }() + + shouldIgnore := false + + if vmessListener != nil { + if vmessListener.Config() != vmessConfig { + vmessListener.Close() + vmessListener = nil + } else { + shouldIgnore = true + } + } + + if shouldIgnore { + return + } + + if len(vmessConfig) == 0 { + return + } + + listener, err := sing_vmess.New(vmessConfig, tcpIn, udpIn) + if err != nil { + return + } + + vmessListener = listener + + return +} + +func ReCreateTcpTun(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { + tcpTunMux.Lock() + defer tcpTunMux.Unlock() + shouldIgnore := false + + var err error + defer func() { + if err != nil { + log.Errorln("Start TcpTun server error: %s", err.Error()) + } + }() + + if tcpTunListener != nil { + if tcpTunListener.Config() != config { + tcpTunListener.Close() + tcpTunListener = nil + } else { + shouldIgnore = true + } + } + + if shouldIgnore { + return + } + + tcpListener, err := tunnel.New(config, tcpIn) + if err != nil { + return + } + + tcpTunListener = tcpListener + + return +} + +func ReCreateUdpTun(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { + udpTunMux.Lock() + defer udpTunMux.Unlock() + shouldIgnore := false + + var err error + defer func() { + if err != nil { + log.Errorln("Start UdpTun server error: %s", err.Error()) + } + }() + + if udpTunListener != nil { + if udpTunListener.Config() != config { + udpTunListener.Close() + udpTunListener = nil + } else { + shouldIgnore = true + } + } + + if shouldIgnore { + return + } + + udpListener, err := tunnel.NewUdp(config, udpIn) + if err != nil { + return + } + + udpTunListener = udpListener + + return +} + func ReCreateTProxy(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { tproxyMux.Lock() defer tproxyMux.Unlock() @@ -490,6 +655,22 @@ func GetPorts() *Ports { ports.MixedPort = port } + if shadowSocksListener != nil { + ports.ShadowSocksConfig = shadowSocksListener.Config() + } + + if vmessListener != nil { + ports.VmessConfig = vmessListener.Config() + } + + if tcpTunListener != nil { + ports.TcpTunConfig = tcpTunListener.Config() + } + + if udpTunListener != nil { + ports.UdpTunConfig = udpTunListener.Config() + } + return ports } diff --git a/listener/sing_shadowsocks/server.go b/listener/sing_shadowsocks/server.go new file mode 100644 index 00000000..cc2c840e --- /dev/null +++ b/listener/sing_shadowsocks/server.go @@ -0,0 +1,173 @@ +package sing_shadowsocks + +import ( + "context" + "fmt" + "net" + "net/url" + "strings" + + "github.com/Dreamacro/clash/adapter/inbound" + "github.com/Dreamacro/clash/common/sockopt" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/listener/sing" + "github.com/Dreamacro/clash/log" + + shadowsocks "github.com/sagernet/sing-shadowsocks" + "github.com/sagernet/sing-shadowsocks/shadowaead" + "github.com/sagernet/sing-shadowsocks/shadowaead_2022" + "github.com/sagernet/sing/common" + "github.com/sagernet/sing/common/buf" + "github.com/sagernet/sing/common/bufio" + "github.com/sagernet/sing/common/metadata" +) + +type Listener struct { + closed bool + config string + listeners []net.Listener + udpListeners []net.PacketConn + service shadowsocks.Service +} + +var _listener *Listener + +func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (*Listener, error) { + addr, cipher, password, err := parseSSURL(config) + if err != nil { + return nil, err + } + udpTimeout := int64(sing.UDPTimeout.Seconds()) + + h := &sing.ListenerHandler{ + TcpIn: tcpIn, + UdpIn: udpIn, + Type: C.SHADOWSOCKS, + } + + sl := &Listener{false, config, nil, nil, nil} + + switch { + case cipher == shadowsocks.MethodNone: + sl.service = shadowsocks.NewNoneService(udpTimeout, h) + case common.Contains(shadowaead.List, cipher): + sl.service, err = shadowaead.NewService(cipher, nil, password, udpTimeout, h) + case common.Contains(shadowaead_2022.List, cipher): + sl.service, err = shadowaead_2022.NewServiceWithPassword(cipher, password, udpTimeout, h) + default: + err = fmt.Errorf("shadowsocks: unsupported method: %s", cipher) + } + if err != nil { + return nil, err + } + + _listener = sl + + for _, addr := range strings.Split(addr, ",") { + addr := addr + + //UDP + ul, err := net.ListenPacket("udp", addr) + if err != nil { + return nil, err + } + + err = sockopt.UDPReuseaddr(ul.(*net.UDPConn)) + if err != nil { + log.Warnln("Failed to Reuse UDP Address: %s", err) + } + + sl.udpListeners = append(sl.udpListeners, ul) + + go func() { + conn := bufio.NewPacketConn(ul) + for { + buff := buf.NewPacket() + remoteAddr, err := conn.ReadPacket(buff) + if err != nil { + buff.Release() + if sl.closed { + break + } + continue + } + _ = sl.service.NewPacket(context.TODO(), conn, buff, metadata.Metadata{ + Protocol: "shadowsocks", + Source: remoteAddr, + }) + } + }() + + //TCP + l, err := net.Listen("tcp", addr) + if err != nil { + return nil, err + } + sl.listeners = append(sl.listeners, l) + + go func() { + log.Infoln("ShadowSocks proxy listening at: %s", l.Addr().String()) + for { + c, err := l.Accept() + if err != nil { + if sl.closed { + break + } + continue + } + _ = c.(*net.TCPConn).SetKeepAlive(true) + + go sl.HandleConn(c) + } + }() + } + + return sl, nil +} + +func (l *Listener) Close() { + l.closed = true + for _, lis := range l.listeners { + _ = lis.Close() + } + for _, lis := range l.udpListeners { + _ = lis.Close() + } +} + +func (l *Listener) Config() string { + return l.config +} + +func (l *Listener) HandleConn(conn net.Conn) { + err := l.service.NewConnection(context.TODO(), conn, metadata.Metadata{ + Protocol: "shadowsocks", + Source: metadata.ParseSocksaddr(conn.RemoteAddr().String()), + }) + if err != nil { + _ = conn.Close() + return + } +} + +func HandleShadowSocks(conn net.Conn) bool { + if _listener != nil && _listener.service != nil { + go _listener.HandleConn(conn) + return true + } + return false +} + +func parseSSURL(s string) (addr, cipher, password string, err error) { + u, err := url.Parse(s) + if err != nil { + return + } + + addr = u.Host + if u.User != nil { + cipher = u.User.Username() + password, _ = u.User.Password() + } + return +} diff --git a/listener/sing_vmess/server.go b/listener/sing_vmess/server.go new file mode 100644 index 00000000..c8d0a2a1 --- /dev/null +++ b/listener/sing_vmess/server.go @@ -0,0 +1,126 @@ +package sing_vmess + +import ( + "context" + "net" + "net/url" + "strings" + + "github.com/Dreamacro/clash/adapter/inbound" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/listener/sing" + "github.com/Dreamacro/clash/log" + + vmess "github.com/sagernet/sing-vmess" + "github.com/sagernet/sing/common/metadata" +) + +type Listener struct { + closed bool + config string + listeners []net.Listener + service *vmess.Service[string] +} + +var _listener *Listener + +func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (*Listener, error) { + addr, username, password, err := parseVmessURL(config) + if err != nil { + return nil, err + } + + h := &sing.ListenerHandler{ + TcpIn: tcpIn, + UdpIn: udpIn, + Type: C.VMESS, + } + + service := vmess.NewService[string](h) + err = service.UpdateUsers([]string{username}, []string{password}, []int{1}) + if err != nil { + return nil, err + } + + err = service.Start() + if err != nil { + return nil, err + } + + sl := &Listener{false, config, nil, service} + _listener = sl + + for _, addr := range strings.Split(addr, ",") { + addr := addr + + //TCP + l, err := net.Listen("tcp", addr) + if err != nil { + return nil, err + } + sl.listeners = append(sl.listeners, l) + + go func() { + log.Infoln("Vmess proxy listening at: %s", l.Addr().String()) + for { + c, err := l.Accept() + if err != nil { + if sl.closed { + break + } + continue + } + _ = c.(*net.TCPConn).SetKeepAlive(true) + + go sl.HandleConn(c) + } + }() + } + + return sl, nil +} + +func (l *Listener) Close() { + l.closed = true + for _, lis := range l.listeners { + _ = lis.Close() + } + _ = l.service.Close() +} + +func (l *Listener) Config() string { + return l.config +} + +func (l *Listener) HandleConn(conn net.Conn) { + err := l.service.NewConnection(context.TODO(), conn, metadata.Metadata{ + Protocol: "vmess", + Source: metadata.ParseSocksaddr(conn.RemoteAddr().String()), + }) + if err != nil { + _ = conn.Close() + return + } +} + +func HandleVmess(conn net.Conn) bool { + if _listener != nil && _listener.service != nil { + go _listener.HandleConn(conn) + return true + } + return false +} + +func parseVmessURL(s string) (addr, username, password string, err error) { + u, err := url.Parse(s) + if err != nil { + return + } + + addr = u.Host + if u.User != nil { + username = u.User.Username() + password, _ = u.User.Password() + } + return +} diff --git a/listener/tunnel/tcp.go b/listener/tunnel/tcp.go new file mode 100644 index 00000000..d51ec47a --- /dev/null +++ b/listener/tunnel/tcp.go @@ -0,0 +1,68 @@ +package tunnel + +import ( + "net" + + "github.com/Dreamacro/clash/adapter/inbound" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/log" + "github.com/Dreamacro/clash/transport/socks5" +) + +type Listener struct { + closed bool + config string + listeners []net.Listener +} + +func New(config string, in chan<- C.ConnContext) (*Listener, error) { + tl := &Listener{false, config, nil} + pl := PairList{} + err := pl.Set(config) + if err != nil { + return nil, err + } + + for _, p := range pl { + addr := p[0] + target := p[1] + go func() { + tgt := socks5.ParseAddr(target) + if tgt == nil { + log.Errorln("invalid target address %q", target) + return + } + l, err := net.Listen("tcp", addr) + if err != nil { + return + } + tl.listeners = append(tl.listeners, l) + log.Infoln("TCP tunnel %s <-> %s", l.Addr().String(), target) + for { + c, err := l.Accept() + if err != nil { + if tl.closed { + break + } + continue + } + _ = c.(*net.TCPConn).SetKeepAlive(true) + + in <- inbound.NewSocket(tgt, c, C.TCPTUN) + } + }() + } + + return tl, nil +} + +func (l *Listener) Close() { + l.closed = true + for _, lis := range l.listeners { + _ = lis.Close() + } +} + +func (l *Listener) Config() string { + return l.config +} diff --git a/listener/tunnel/udp.go b/listener/tunnel/udp.go new file mode 100644 index 00000000..1f43122b --- /dev/null +++ b/listener/tunnel/udp.go @@ -0,0 +1,79 @@ +package tunnel + +import ( + "net" + + "github.com/Dreamacro/clash/adapter/inbound" + "github.com/Dreamacro/clash/common/pool" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/log" + "github.com/Dreamacro/clash/transport/socks5" +) + +type UdpListener struct { + closed bool + config string + listeners []net.PacketConn +} + +func NewUdp(config string, in chan<- *inbound.PacketAdapter) (*UdpListener, error) { + ul := &UdpListener{false, config, nil} + pl := PairList{} + err := pl.Set(config) + if err != nil { + return nil, err + } + + for _, p := range pl { + addr := p[0] + target := p[1] + go func() { + tgt := socks5.ParseAddr(target) + if tgt == nil { + log.Errorln("invalid target address %q", target) + return + } + l, err := net.ListenPacket("udp", addr) + if err != nil { + return + } + ul.listeners = append(ul.listeners, l) + log.Infoln("Udp tunnel %s <-> %s", l.LocalAddr().String(), target) + for { + buf := pool.Get(pool.RelayBufferSize) + n, remoteAddr, err := l.ReadFrom(buf) + if err != nil { + pool.Put(buf) + if ul.closed { + break + } + continue + } + packet := &packet{ + pc: l, + rAddr: remoteAddr, + payload: buf[:n], + bufRef: buf, + } + select { + case in <- inbound.NewPacket(tgt, packet, C.UDPTUN): + default: + } + + } + }() + } + + return ul, nil +} + +func (l *UdpListener) Close() { + l.closed = true + for _, lis := range l.listeners { + _ = lis.Close() + } +} + +func (l *UdpListener) Config() string { + return l.config +} diff --git a/listener/tunnel/utils.go b/listener/tunnel/utils.go new file mode 100644 index 00000000..7cce7406 --- /dev/null +++ b/listener/tunnel/utils.go @@ -0,0 +1,63 @@ +package tunnel + +import ( + "errors" + "net" + "strings" + + "github.com/Dreamacro/clash/common/pool" +) + +type PairList [][2]string // key1=val1,key2=val2,... + +func (l PairList) String() string { + s := make([]string, len(l)) + for i, pair := range l { + s[i] = pair[0] + "=" + pair[1] + } + return strings.Join(s, ",") +} +func (l *PairList) Set(s string) error { + for _, item := range strings.Split(s, ",") { + pair := strings.Split(item, "=") + if len(pair) != 2 { + return nil + } + *l = append(*l, [2]string{pair[0], pair[1]}) + } + return nil +} + +type packet struct { + pc net.PacketConn + rAddr net.Addr + payload []byte + bufRef []byte +} + +func (c *packet) Data() []byte { + return c.payload +} + +// WriteBack wirtes UDP packet with source(ip, port) = `addr` +func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) { + if addr == nil { + err = errors.New("address is invalid") + return + } + packet := b + return c.pc.WriteTo(packet, c.rAddr) +} + +// LocalAddr returns the source IP/Port of UDP Packet +func (c *packet) LocalAddr() net.Addr { + return c.rAddr +} + +func (c *packet) Drop() { + pool.Put(c.bufRef) +} + +func (c *packet) InAddr() net.Addr { + return c.pc.LocalAddr() +} From 68b28ed530d8234b2240a8c62939f9b9e93b54ab Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 11 Nov 2022 22:44:44 +0800 Subject: [PATCH 095/250] chore: shadowsocks listener support old cipher --- constant/listener.go | 8 +++ listener/listener.go | 2 +- listener/shadowsocks/tcp.go | 105 ++++++++++++++++++++++++++++ listener/shadowsocks/udp.go | 76 ++++++++++++++++++++ listener/shadowsocks/utils.go | 59 ++++++++++++++++ listener/sing_shadowsocks/server.go | 14 ++-- listener/sing_vmess/server.go | 8 +-- 7 files changed, 261 insertions(+), 11 deletions(-) create mode 100644 listener/shadowsocks/tcp.go create mode 100644 listener/shadowsocks/udp.go create mode 100644 listener/shadowsocks/utils.go diff --git a/constant/listener.go b/constant/listener.go index 07782a9e..08a590bd 100644 --- a/constant/listener.go +++ b/constant/listener.go @@ -1,7 +1,15 @@ package constant +import "net" + type Listener interface { RawAddress() string Address() string Close() error } + +type AdvanceListener interface { + Close() + Config() string + HandleConn(conn net.Conn, in chan<- ConnContext) +} diff --git a/listener/listener.go b/listener/listener.go index c9e95226..880f56d4 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -41,7 +41,7 @@ var ( mixedListener *mixed.Listener mixedUDPLister *socks.UDPListener tunLister *sing_tun.Listener - shadowSocksListener *sing_shadowsocks.Listener + shadowSocksListener C.AdvanceListener vmessListener *sing_vmess.Listener tcpTunListener *tunnel.Listener udpTunListener *tunnel.UdpListener diff --git a/listener/shadowsocks/tcp.go b/listener/shadowsocks/tcp.go new file mode 100644 index 00000000..d767b8ab --- /dev/null +++ b/listener/shadowsocks/tcp.go @@ -0,0 +1,105 @@ +package shadowsocks + +import ( + "net" + "strings" + + "github.com/Dreamacro/clash/adapter/inbound" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/log" + "github.com/Dreamacro/clash/transport/shadowsocks/core" + "github.com/Dreamacro/clash/transport/socks5" +) + +type Listener struct { + closed bool + config string + listeners []net.Listener + udpListeners []*UDPListener + pickCipher core.Cipher +} + +var _listener *Listener + +func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (*Listener, error) { + addr, cipher, password, err := parseSSURL(config) + if err != nil { + return nil, err + } + + pickCipher, err := core.PickCipher(cipher, nil, password) + if err != nil { + return nil, err + } + + sl := &Listener{false, config, nil, nil, pickCipher} + _listener = sl + + for _, addr := range strings.Split(addr, ",") { + addr := addr + + //UDP + ul, err := NewUDP(addr, pickCipher, udpIn) + if err != nil { + return nil, err + } + sl.udpListeners = append(sl.udpListeners, ul) + + //TCP + l, err := net.Listen("tcp", addr) + if err != nil { + return nil, err + } + sl.listeners = append(sl.listeners, l) + + go func() { + log.Infoln("ShadowSocks proxy listening at: %s", l.Addr().String()) + for { + c, err := l.Accept() + if err != nil { + if sl.closed { + break + } + continue + } + _ = c.(*net.TCPConn).SetKeepAlive(true) + go sl.HandleConn(c, tcpIn) + } + }() + } + + return sl, nil +} + +func (l *Listener) Close() { + l.closed = true + for _, lis := range l.listeners { + _ = lis.Close() + } + for _, lis := range l.udpListeners { + _ = lis.Close() + } +} + +func (l *Listener) Config() string { + return l.config +} + +func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext) { + conn = l.pickCipher.StreamConn(conn) + + target, err := socks5.ReadAddr(conn, make([]byte, socks5.MaxAddrLen)) + if err != nil { + _ = conn.Close() + return + } + in <- inbound.NewSocket(target, conn, C.SHADOWSOCKS) +} + +func HandleShadowSocks(conn net.Conn, in chan<- C.ConnContext) bool { + if _listener != nil && _listener.pickCipher != nil { + go _listener.HandleConn(conn, in) + return true + } + return false +} diff --git a/listener/shadowsocks/udp.go b/listener/shadowsocks/udp.go new file mode 100644 index 00000000..efb29e41 --- /dev/null +++ b/listener/shadowsocks/udp.go @@ -0,0 +1,76 @@ +package shadowsocks + +import ( + "net" + + "github.com/Dreamacro/clash/adapter/inbound" + "github.com/Dreamacro/clash/common/pool" + "github.com/Dreamacro/clash/common/sockopt" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/log" + "github.com/Dreamacro/clash/transport/shadowsocks/core" + "github.com/Dreamacro/clash/transport/socks5" +) + +type UDPListener struct { + packetConn net.PacketConn + closed bool +} + +func NewUDP(addr string, pickCipher core.Cipher, in chan<- *inbound.PacketAdapter) (*UDPListener, error) { + l, err := net.ListenPacket("udp", addr) + if err != nil { + return nil, err + } + + err = sockopt.UDPReuseaddr(l.(*net.UDPConn)) + if err != nil { + log.Warnln("Failed to Reuse UDP Address: %s", err) + } + + sl := &UDPListener{l, false} + conn := pickCipher.PacketConn(l) + go func() { + for { + buf := pool.Get(pool.RelayBufferSize) + n, remoteAddr, err := conn.ReadFrom(buf) + if err != nil { + pool.Put(buf) + if sl.closed { + break + } + continue + } + handleSocksUDP(conn, in, buf[:n], remoteAddr) + } + }() + + return sl, nil +} + +func (l *UDPListener) Close() error { + l.closed = true + return l.packetConn.Close() +} + +func handleSocksUDP(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buf []byte, addr net.Addr) { + tgtAddr := socks5.SplitAddr(buf) + if tgtAddr == nil { + // Unresolved UDP packet, return buffer to the pool + pool.Put(buf) + return + } + target := socks5.ParseAddr(tgtAddr.String()) + payload := buf[len(tgtAddr):] + + packet := &packet{ + pc: pc, + rAddr: addr, + payload: payload, + bufRef: buf, + } + select { + case in <- inbound.NewPacket(target, packet, C.SHADOWSOCKS): + default: + } +} diff --git a/listener/shadowsocks/utils.go b/listener/shadowsocks/utils.go new file mode 100644 index 00000000..f1710c94 --- /dev/null +++ b/listener/shadowsocks/utils.go @@ -0,0 +1,59 @@ +package shadowsocks + +import ( + "bytes" + "errors" + "net" + "net/url" + + "github.com/Dreamacro/clash/common/pool" + "github.com/Dreamacro/clash/transport/socks5" +) + +type packet struct { + pc net.PacketConn + rAddr net.Addr + payload []byte + bufRef []byte +} + +func (c *packet) Data() []byte { + return c.payload +} + +// WriteBack wirtes UDP packet with source(ip, port) = `addr` +func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) { + if addr == nil { + err = errors.New("address is invalid") + return + } + packet := bytes.Join([][]byte{socks5.ParseAddrToSocksAddr(addr), b}, []byte{}) + return c.pc.WriteTo(packet, c.rAddr) +} + +// LocalAddr returns the source IP/Port of UDP Packet +func (c *packet) LocalAddr() net.Addr { + return c.rAddr +} + +func (c *packet) Drop() { + pool.Put(c.bufRef) +} + +func (c *packet) InAddr() net.Addr { + return c.pc.LocalAddr() +} + +func parseSSURL(s string) (addr, cipher, password string, err error) { + u, err := url.Parse(s) + if err != nil { + return + } + + addr = u.Host + if u.User != nil { + cipher = u.User.Username() + password, _ = u.User.Password() + } + return +} diff --git a/listener/sing_shadowsocks/server.go b/listener/sing_shadowsocks/server.go index cc2c840e..7bb9bb54 100644 --- a/listener/sing_shadowsocks/server.go +++ b/listener/sing_shadowsocks/server.go @@ -10,6 +10,7 @@ import ( "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/common/sockopt" C "github.com/Dreamacro/clash/constant" + embedSS "github.com/Dreamacro/clash/listener/shadowsocks" "github.com/Dreamacro/clash/listener/sing" "github.com/Dreamacro/clash/log" @@ -32,7 +33,7 @@ type Listener struct { var _listener *Listener -func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (*Listener, error) { +func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (C.AdvanceListener, error) { addr, cipher, password, err := parseSSURL(config) if err != nil { return nil, err @@ -56,6 +57,7 @@ func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.Packet sl.service, err = shadowaead_2022.NewServiceWithPassword(cipher, password, udpTimeout, h) default: err = fmt.Errorf("shadowsocks: unsupported method: %s", cipher) + return embedSS.New(config, tcpIn, udpIn) } if err != nil { return nil, err @@ -117,7 +119,7 @@ func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.Packet } _ = c.(*net.TCPConn).SetKeepAlive(true) - go sl.HandleConn(c) + go sl.HandleConn(c, tcpIn) } }() } @@ -139,7 +141,7 @@ func (l *Listener) Config() string { return l.config } -func (l *Listener) HandleConn(conn net.Conn) { +func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext) { err := l.service.NewConnection(context.TODO(), conn, metadata.Metadata{ Protocol: "shadowsocks", Source: metadata.ParseSocksaddr(conn.RemoteAddr().String()), @@ -150,12 +152,12 @@ func (l *Listener) HandleConn(conn net.Conn) { } } -func HandleShadowSocks(conn net.Conn) bool { +func HandleShadowSocks(conn net.Conn, in chan<- C.ConnContext) bool { if _listener != nil && _listener.service != nil { - go _listener.HandleConn(conn) + go _listener.HandleConn(conn, in) return true } - return false + return embedSS.HandleShadowSocks(conn, in) } func parseSSURL(s string) (addr, cipher, password string, err error) { diff --git a/listener/sing_vmess/server.go b/listener/sing_vmess/server.go index c8d0a2a1..26b12893 100644 --- a/listener/sing_vmess/server.go +++ b/listener/sing_vmess/server.go @@ -72,7 +72,7 @@ func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.Packet } _ = c.(*net.TCPConn).SetKeepAlive(true) - go sl.HandleConn(c) + go sl.HandleConn(c, tcpIn) } }() } @@ -92,7 +92,7 @@ func (l *Listener) Config() string { return l.config } -func (l *Listener) HandleConn(conn net.Conn) { +func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext) { err := l.service.NewConnection(context.TODO(), conn, metadata.Metadata{ Protocol: "vmess", Source: metadata.ParseSocksaddr(conn.RemoteAddr().String()), @@ -103,9 +103,9 @@ func (l *Listener) HandleConn(conn net.Conn) { } } -func HandleVmess(conn net.Conn) bool { +func HandleVmess(conn net.Conn, in chan<- C.ConnContext) bool { if _listener != nil && _listener.service != nil { - go _listener.HandleConn(conn) + go _listener.HandleConn(conn, in) return true } return false From 64be213b665c283f58d938a12f45b781483f3c3d Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 11 Nov 2022 22:48:44 +0800 Subject: [PATCH 096/250] code cleanup --- listener/shadowsocks/tcp.go | 2 +- listener/shadowsocks/utils.go | 2 +- listener/sing_shadowsocks/server.go | 17 +---------------- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/listener/shadowsocks/tcp.go b/listener/shadowsocks/tcp.go index d767b8ab..b540a632 100644 --- a/listener/shadowsocks/tcp.go +++ b/listener/shadowsocks/tcp.go @@ -22,7 +22,7 @@ type Listener struct { var _listener *Listener func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (*Listener, error) { - addr, cipher, password, err := parseSSURL(config) + addr, cipher, password, err := ParseSSURL(config) if err != nil { return nil, err } diff --git a/listener/shadowsocks/utils.go b/listener/shadowsocks/utils.go index f1710c94..2e9fd003 100644 --- a/listener/shadowsocks/utils.go +++ b/listener/shadowsocks/utils.go @@ -44,7 +44,7 @@ func (c *packet) InAddr() net.Addr { return c.pc.LocalAddr() } -func parseSSURL(s string) (addr, cipher, password string, err error) { +func ParseSSURL(s string) (addr, cipher, password string, err error) { u, err := url.Parse(s) if err != nil { return diff --git a/listener/sing_shadowsocks/server.go b/listener/sing_shadowsocks/server.go index 7bb9bb54..ba61ea36 100644 --- a/listener/sing_shadowsocks/server.go +++ b/listener/sing_shadowsocks/server.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "net" - "net/url" "strings" "github.com/Dreamacro/clash/adapter/inbound" @@ -34,7 +33,7 @@ type Listener struct { var _listener *Listener func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (C.AdvanceListener, error) { - addr, cipher, password, err := parseSSURL(config) + addr, cipher, password, err := embedSS.ParseSSURL(config) if err != nil { return nil, err } @@ -159,17 +158,3 @@ func HandleShadowSocks(conn net.Conn, in chan<- C.ConnContext) bool { } return embedSS.HandleShadowSocks(conn, in) } - -func parseSSURL(s string) (addr, cipher, password string, err error) { - u, err := url.Parse(s) - if err != nil { - return - } - - addr = u.Host - if u.User != nil { - cipher = u.User.Username() - password, _ = u.User.Password() - } - return -} From b2d7149a958640ffe2cda2d7c41454669e76e5e1 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 11 Nov 2022 23:36:06 +0800 Subject: [PATCH 097/250] chore: support IN-PORT rule --- adapter/inbound/http.go | 4 ++++ adapter/inbound/https.go | 4 ++++ adapter/inbound/packet.go | 6 ++++++ adapter/inbound/socket.go | 8 ++++++++ constant/adapters.go | 4 ++++ constant/metadata.go | 2 ++ constant/rule.go | 3 +++ listener/socks/utils.go | 4 ++++ listener/tproxy/packet.go | 5 +++++ listener/tproxy/udp.go | 1 + rules/common/port.go | 21 +++++++++++---------- rules/logic/logic_test.go | 6 ++++-- rules/parser.go | 6 ++++-- 13 files changed, 60 insertions(+), 14 deletions(-) diff --git a/adapter/inbound/http.go b/adapter/inbound/http.go index 89960cf3..7ef880d1 100644 --- a/adapter/inbound/http.go +++ b/adapter/inbound/http.go @@ -17,5 +17,9 @@ func NewHTTP(target socks5.Addr, source net.Addr, conn net.Conn) *context.ConnCo metadata.SrcIP = ip metadata.SrcPort = port } + if ip, port, err := parseAddr(conn.LocalAddr().String()); err == nil { + metadata.InIP = ip + metadata.InPort = port + } return context.NewConnContext(conn, metadata) } diff --git a/adapter/inbound/https.go b/adapter/inbound/https.go index 99bc433f..53ad46b2 100644 --- a/adapter/inbound/https.go +++ b/adapter/inbound/https.go @@ -16,5 +16,9 @@ func NewHTTPS(request *http.Request, conn net.Conn) *context.ConnContext { metadata.SrcIP = ip metadata.SrcPort = port } + if ip, port, err := parseAddr(conn.LocalAddr().String()); err == nil { + metadata.InIP = ip + metadata.InPort = port + } return context.NewConnContext(conn, metadata) } diff --git a/adapter/inbound/packet.go b/adapter/inbound/packet.go index 80b136cd..687e1089 100644 --- a/adapter/inbound/packet.go +++ b/adapter/inbound/packet.go @@ -25,6 +25,12 @@ func NewPacket(target socks5.Addr, packet C.UDPPacket, source C.Type) *PacketAda metadata.SrcIP = ip metadata.SrcPort = port } + if p, ok := packet.(C.UDPPacketInAddr); ok { + if ip, port, err := parseAddr(p.InAddr().String()); err == nil { + metadata.InIP = ip + metadata.InPort = port + } + } return &PacketAdapter{ UDPPacket: packet, diff --git a/adapter/inbound/socket.go b/adapter/inbound/socket.go index 7fe00dbf..63231611 100644 --- a/adapter/inbound/socket.go +++ b/adapter/inbound/socket.go @@ -22,6 +22,14 @@ func NewSocket(target socks5.Addr, conn net.Conn, source C.Type) *context.ConnCo metadata.SrcPort = port } } + localAddr := conn.LocalAddr() + // Filter when net.Addr interface is nil + if localAddr != nil { + if ip, port, err := parseAddr(localAddr.String()); err == nil { + metadata.InIP = ip + metadata.InPort = port + } + } return context.NewConnContext(conn, metadata) } diff --git a/constant/adapters.go b/constant/adapters.go index 71613487..47826a74 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -202,3 +202,7 @@ type UDPPacket interface { // LocalAddr returns the source IP/Port of packet LocalAddr() net.Addr } + +type UDPPacketInAddr interface { + InAddr() net.Addr +} diff --git a/constant/metadata.go b/constant/metadata.go index 61a98be3..dcddcd8b 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -115,6 +115,8 @@ type Metadata struct { DstIP netip.Addr `json:"destinationIP"` SrcPort string `json:"sourcePort"` DstPort string `json:"destinationPort"` + InIP netip.Addr `json:"inboundIP"` + InPort string `json:"inboundPort"` Host string `json:"host"` DNSMode DNSMode `json:"dnsMode"` Uid *int32 `json:"uid"` diff --git a/constant/rule.go b/constant/rule.go index 223dec6e..28c629a0 100644 --- a/constant/rule.go +++ b/constant/rule.go @@ -13,6 +13,7 @@ const ( SrcIPSuffix SrcPort DstPort + InPort Process ProcessPath RuleSet @@ -52,6 +53,8 @@ func (rt RuleType) String() string { return "SrcPort" case DstPort: return "DstPort" + case InPort: + return "InPort" case Process: return "Process" case ProcessPath: diff --git a/listener/socks/utils.go b/listener/socks/utils.go index 28dfef72..4c53b9e5 100644 --- a/listener/socks/utils.go +++ b/listener/socks/utils.go @@ -35,3 +35,7 @@ func (c *packet) LocalAddr() net.Addr { func (c *packet) Drop() { pool.Put(c.bufRef) } + +func (c *packet) InAddr() net.Addr { + return c.pc.LocalAddr() +} diff --git a/listener/tproxy/packet.go b/listener/tproxy/packet.go index 8aa3e9bf..d60f0ac2 100644 --- a/listener/tproxy/packet.go +++ b/listener/tproxy/packet.go @@ -7,6 +7,7 @@ import ( ) type packet struct { + pc net.PacketConn lAddr *net.UDPAddr buf []byte } @@ -35,3 +36,7 @@ func (c *packet) LocalAddr() net.Addr { func (c *packet) Drop() { pool.Put(c.buf) } + +func (c *packet) InAddr() net.Addr { + return c.pc.LocalAddr() +} diff --git a/listener/tproxy/udp.go b/listener/tproxy/udp.go index c7e6d99e..57120785 100644 --- a/listener/tproxy/udp.go +++ b/listener/tproxy/udp.go @@ -81,6 +81,7 @@ func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (*UDPListener, error) func handlePacketConn(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buf []byte, lAddr *net.UDPAddr, rAddr *net.UDPAddr) { target := socks5.ParseAddrToSocksAddr(rAddr) pkt := &packet{ + pc: pc, lAddr: lAddr, buf: buf, } diff --git a/rules/common/port.go b/rules/common/port.go index 270e5b20..3b7ea1fc 100644 --- a/rules/common/port.go +++ b/rules/common/port.go @@ -13,22 +13,23 @@ type Port struct { *Base adapter string port string - isSource bool + ruleType C.RuleType portList []utils.Range[uint16] } func (p *Port) RuleType() C.RuleType { - if p.isSource { - return C.SrcPort - } - return C.DstPort + return p.ruleType } func (p *Port) Match(metadata *C.Metadata) (bool, string) { - if p.isSource { - return p.matchPortReal(metadata.SrcPort), p.adapter + targetPort := metadata.DstPort + switch p.ruleType { + case C.InPort: + targetPort = metadata.InPort + case C.SrcPort: + targetPort = metadata.SrcPort } - return p.matchPortReal(metadata.DstPort), p.adapter + return p.matchPortReal(targetPort), p.adapter } func (p *Port) Adapter() string { @@ -51,7 +52,7 @@ func (p *Port) matchPortReal(portRef string) bool { return false } -func NewPort(port string, adapter string, isSource bool) (*Port, error) { +func NewPort(port string, adapter string, ruleType C.RuleType) (*Port, error) { ports := strings.Split(port, "/") if len(ports) > 28 { return nil, fmt.Errorf("%s, too many ports to use, maximum support 28 ports", errPayload.Error()) @@ -95,7 +96,7 @@ func NewPort(port string, adapter string, isSource bool) (*Port, error) { Base: &Base{}, adapter: adapter, port: port, - isSource: isSource, + ruleType: ruleType, portList: portRange, }, nil } diff --git a/rules/logic/logic_test.go b/rules/logic/logic_test.go index 0410873e..da061d71 100644 --- a/rules/logic/logic_test.go +++ b/rules/logic/logic_test.go @@ -33,9 +33,11 @@ func ParseRule(tp, payload, target string, params []string, subRules *map[string case "SRC-IP-SUFFIX": parsed, parseErr = RC.NewIPSuffix(payload, target, true, true) case "SRC-PORT": - parsed, parseErr = RC.NewPort(payload, target, true) + parsed, parseErr = RC.NewPort(payload, target, C.SrcPort) case "DST-PORT": - parsed, parseErr = RC.NewPort(payload, target, false) + parsed, parseErr = RC.NewPort(payload, target, C.DstPort) + case "IN-PORT": + parsed, parseErr = RC.NewPort(payload, target, C.InPort) case "PROCESS-NAME": parsed, parseErr = RC.NewProcess(payload, target, true) case "PROCESS-PATH": diff --git a/rules/parser.go b/rules/parser.go index 6f9e229f..4e1d9044 100644 --- a/rules/parser.go +++ b/rules/parser.go @@ -32,9 +32,11 @@ func ParseRule(tp, payload, target string, params []string, subRules *map[string case "SRC-IP-SUFFIX": parsed, parseErr = RC.NewIPSuffix(payload, target, true, true) case "SRC-PORT": - parsed, parseErr = RC.NewPort(payload, target, true) + parsed, parseErr = RC.NewPort(payload, target, C.SrcPort) case "DST-PORT": - parsed, parseErr = RC.NewPort(payload, target, false) + parsed, parseErr = RC.NewPort(payload, target, C.DstPort) + case "IN-PORT": + parsed, parseErr = RC.NewPort(payload, target, C.InPort) case "PROCESS-NAME": parsed, parseErr = RC.NewProcess(payload, target, true) case "PROCESS-PATH": From 3e20912339ed03b1aab939892d8dad5d2ccf2968 Mon Sep 17 00:00:00 2001 From: Skimmle Date: Sat, 12 Nov 2022 11:14:51 +0800 Subject: [PATCH 098/250] featrue: DoH and DoQ are implemented using AdGuardTeam/dnsProxy, DoH support perfer and force http3 --- config/config.go | 27 +- constant/dns.go | 11 + dns/doh.go | 810 ++++++++++++++++++++++++++++++++++++++++------- dns/doq.go | 535 ++++++++++++++++++++++++------- dns/resolver.go | 1 + dns/util.go | 12 +- 6 files changed, 1152 insertions(+), 244 deletions(-) diff --git a/config/config.go b/config/config.go index 57bf55e8..f1a85969 100644 --- a/config/config.go +++ b/config/config.go @@ -863,7 +863,7 @@ func hostWithDefaultPort(host string, defPort string) (string, error) { return net.JoinHostPort(hostname, port), nil } -func parseNameServer(servers []string) ([]dns.NameServer, error) { +func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error) { var nameservers []dns.NameServer for idx, server := range servers { @@ -889,7 +889,15 @@ func parseNameServer(servers []string) ([]dns.NameServer, error) { addr, err = hostWithDefaultPort(u.Host, "853") dnsNetType = "tcp-tls" // DNS over TLS case "https": - clearURL := url.URL{Scheme: "https", Host: u.Host, Path: u.Path} + host := u.Host + if _, _, err := net.SplitHostPort(host); err != nil && strings.Contains(err.Error(), "missing port in address") { + host = net.JoinHostPort(host, "443") + } else { + if err!=nil{ + return nil,err + } + } + clearURL := url.URL{Scheme: "https", Host: host, Path: u.Path} addr = clearURL.String() dnsNetType = "https" // DNS over HTTPS if len(u.Fragment) != 0 { @@ -928,17 +936,18 @@ func parseNameServer(servers []string) ([]dns.NameServer, error) { ProxyAdapter: proxyAdapter, Interface: dialer.DefaultInterface, Params: params, + PreferH3: preferH3, }, ) } return nameservers, nil } -func parseNameServerPolicy(nsPolicy map[string]string) (map[string]dns.NameServer, error) { +func parseNameServerPolicy(nsPolicy map[string]string, preferH3 bool) (map[string]dns.NameServer, error) { policy := map[string]dns.NameServer{} for domain, server := range nsPolicy { - nameservers, err := parseNameServer([]string{server}) + nameservers, err := parseNameServer([]string{server}, preferH3) if err != nil { return nil, err } @@ -1018,26 +1027,26 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[netip.Addr], rules []C.R }, } var err error - if dnsCfg.NameServer, err = parseNameServer(cfg.NameServer); err != nil { + if dnsCfg.NameServer, err = parseNameServer(cfg.NameServer, cfg.PreferH3); err != nil { return nil, err } - if dnsCfg.Fallback, err = parseNameServer(cfg.Fallback); err != nil { + if dnsCfg.Fallback, err = parseNameServer(cfg.Fallback, cfg.PreferH3); err != nil { return nil, err } - if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy); err != nil { + if dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy, cfg.PreferH3); err != nil { return nil, err } - if dnsCfg.ProxyServerNameserver, err = parseNameServer(cfg.ProxyServerNameserver); err != nil { + if dnsCfg.ProxyServerNameserver, err = parseNameServer(cfg.ProxyServerNameserver, cfg.PreferH3); err != nil { return nil, err } if len(cfg.DefaultNameserver) == 0 { return nil, errors.New("default nameserver should have at least one nameserver") } - if dnsCfg.DefaultNameserver, err = parseNameServer(cfg.DefaultNameserver); err != nil { + if dnsCfg.DefaultNameserver, err = parseNameServer(cfg.DefaultNameserver, cfg.PreferH3); err != nil { return nil, err } // check default nameserver is pure ip addr diff --git a/constant/dns.go b/constant/dns.go index be8b4a17..da68753c 100644 --- a/constant/dns.go +++ b/constant/dns.go @@ -114,3 +114,14 @@ func NewDNSPrefer(prefer string) DNSPrefer { return DualStack } } + +type HTTPVersion string + +const ( + // HTTPVersion11 is HTTP/1.1. + HTTPVersion11 HTTPVersion = "http/1.1" + // HTTPVersion2 is HTTP/2. + HTTPVersion2 HTTPVersion = "h2" + // HTTPVersion3 is HTTP/3. + HTTPVersion3 HTTPVersion = "h3" +) \ No newline at end of file diff --git a/dns/doh.go b/dns/doh.go index 8403f7d1..d5a8b06e 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -1,164 +1,730 @@ package dns import ( - "bytes" "context" "crypto/tls" + "encoding/base64" + "errors" "fmt" - "github.com/Dreamacro/clash/component/dialer" - "github.com/Dreamacro/clash/component/resolver" - tlsC "github.com/Dreamacro/clash/component/tls" - "github.com/lucas-clemente/quic-go" - "github.com/lucas-clemente/quic-go/http3" - D "github.com/miekg/dns" "io" "net" "net/http" + "net/url" + "runtime" "strconv" + "sync" + "time" + + "github.com/Dreamacro/clash/component/dialer" + tlsC "github.com/Dreamacro/clash/component/tls" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/log" + "github.com/lucas-clemente/quic-go" + "github.com/lucas-clemente/quic-go/http3" + "github.com/miekg/dns" + D "github.com/miekg/dns" + "golang.org/x/net/http2" ) +// Values to configure HTTP and HTTP/2 transport. const ( - // dotMimeType is the DoH mimetype that should be used. - dotMimeType = "application/dns-message" + // transportDefaultReadIdleTimeout is the default timeout for pinging + // idle connections in HTTP/2 transport. + transportDefaultReadIdleTimeout = 30 * time.Second + + // transportDefaultIdleConnTimeout is the default timeout for idle + // connections in HTTP transport. + transportDefaultIdleConnTimeout = 5 * time.Minute + + // dohMaxConnsPerHost controls the maximum number of connections for + // each host. + dohMaxConnsPerHost = 1 + dialTimeout = 10 * time.Second + + // dohMaxIdleConns controls the maximum number of connections being idle + // at the same time. + dohMaxIdleConns = 1 + maxElapsedTime = time.Second * 30 ) -type dohClient struct { - url string - transport http.RoundTripper +var DefaultHTTPVersions = []C.HTTPVersion{C.HTTPVersion11, C.HTTPVersion2} + +// dnsOverHTTPS is a struct that implements the Upstream interface for the +// DNS-over-HTTPS protocol. +type dnsOverHTTPS struct { + // The Client's Transport typically has internal state (cached TCP + // connections), so Clients should be reused instead of created as + // needed. Clients are safe for concurrent use by multiple goroutines. + client *http.Client + clientMu sync.Mutex + + // quicConfig is the QUIC configuration that is used if HTTP/3 is enabled + // for this upstream. + quicConfig *quic.Config + quicConfigGuard sync.Mutex + url *url.URL + r *Resolver + httpVersions []C.HTTPVersion + proxyAdapter string } -func (dc *dohClient) Exchange(m *D.Msg) (msg *D.Msg, err error) { - return dc.ExchangeContext(context.Background(), m) +// type check +var _ dnsClient = (*dnsOverHTTPS)(nil) + +// newDoH returns the DNS-over-HTTPS Upstream. +func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[string]string, proxyAdapter string) dnsClient { + u, _ := url.Parse(urlString) + httpVersions := DefaultHTTPVersions + if preferH3 { + httpVersions = append(httpVersions, C.HTTPVersion3) + } + + if params["h3"] == "true" { + httpVersions = []C.HTTPVersion{C.HTTPVersion3} + } + + doh := &dnsOverHTTPS{ + url: u, + r: r, + quicConfig: &quic.Config{ + KeepAlivePeriod: QUICKeepAlivePeriod, + TokenStore: newQUICTokenStore(), + }, + httpVersions: httpVersions, + } + + runtime.SetFinalizer(doh, (*dnsOverHTTPS).Close) + + return doh } -func (dc *dohClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { - // https://datatracker.ietf.org/doc/html/rfc8484#section-4.1 - // In order to maximize cache friendliness, SHOULD use a DNS ID of 0 in every DNS request. - newM := *m - newM.Id = 0 - req, err := dc.newRequest(&newM) +// Address implements the Upstream interface for *dnsOverHTTPS. +func (p *dnsOverHTTPS) Address() string { return p.url.String() } +func (p *dnsOverHTTPS) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { + // Quote from https://www.rfc-editor.org/rfc/rfc8484.html: + // In order to maximize HTTP cache friendliness, DoH clients using media + // formats that include the ID field from the DNS message header, such + // as "application/dns-message", SHOULD use a DNS ID of 0 in every DNS + // request. + id := m.Id + m.Id = 0 + defer func() { + // Restore the original ID to not break compatibility with proxies. + m.Id = id + if msg != nil { + msg.Id = id + } + }() + + // Check if there was already an active client before sending the request. + // We'll only attempt to re-connect if there was one. + client, isCached, err := p.getClient() if err != nil { - return nil, err + return nil, fmt.Errorf("failed to init http client: %w", err) } - req = req.WithContext(ctx) - msg, err = dc.doRequest(req) - if err == nil { - msg.Id = m.Id - } - return -} + // Make the first attempt to send the DNS query. + msg, err = p.exchangeHTTPS(ctx, client, m) + + // Make up to 2 attempts to re-create the HTTP client and send the request + // again. There are several cases (mostly, with QUIC) where this workaround + // is necessary to make HTTP client usable. We need to make 2 attempts in + // the case when the connection was closed (due to inactivity for example) + // AND the server refuses to open a 0-RTT connection. + for i := 0; isCached && p.shouldRetry(err) && i < 2; i++ { + client, err = p.resetClient(err) + if err != nil { + return nil, fmt.Errorf("failed to reset http client: %w", err) + } + + msg, err = p.exchangeHTTPS(ctx, client, m) + } -// newRequest returns a new DoH request given a dns.Msg. -func (dc *dohClient) newRequest(m *D.Msg) (*http.Request, error) { - buf, err := m.Pack() if err != nil { - return nil, err + // If the request failed anyway, make sure we don't use this client. + _, resErr := p.resetClient(err) + + return nil, fmt.Errorf("err:%v,resErr:%v", err, resErr) } - req, err := http.NewRequest(http.MethodPost, dc.url, bytes.NewReader(buf)) - if err != nil { - return req, err - } - - req.Header.Set("content-type", dotMimeType) - req.Header.Set("accept", dotMimeType) - return req, nil -} - -func (dc *dohClient) doRequest(req *http.Request) (msg *D.Msg, err error) { - client := &http.Client{Transport: dc.transport} - resp, err := client.Do(req) - if err != nil { - return nil, err - } - - defer resp.Body.Close() - - buf, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - msg = &D.Msg{} - err = msg.Unpack(buf) return msg, err } -func newDoHClient(url string, r *Resolver, params map[string]string, proxyAdapter string) *dohClient { - useH3 := params["h3"] == "true" - TLCConfig := tlsC.GetDefaultTLSConfig() - var transport http.RoundTripper - if useH3 { - transport = &http3.RoundTripper{ - Dial: func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) { - host, port, err := net.SplitHostPort(addr) - if err != nil { - return nil, err - } +// Exchange implements the Upstream interface for *dnsOverHTTPS. +func (p *dnsOverHTTPS) Exchange(m *dns.Msg) (*dns.Msg, error) { + return p.ExchangeContext(context.Background(), m) +} - ip, err := resolver.ResolveIPWithResolver(host, r) - if err != nil { - return nil, err - } +// Close implements the Upstream interface for *dnsOverHTTPS. +func (p *dnsOverHTTPS) Close() (err error) { + p.clientMu.Lock() + defer p.clientMu.Unlock() - portInt, err := strconv.Atoi(port) - if err != nil { - return nil, err - } + runtime.SetFinalizer(p, nil) - udpAddr := net.UDPAddr{ - IP: net.ParseIP(ip.String()), - Port: portInt, - } + if p.client == nil { + return nil + } - var conn net.PacketConn - if proxyAdapter == "" { - conn, err = dialer.ListenPacket(ctx, "udp", "") - if err != nil { - return nil, err - } - } else { - if wrapConn, err := dialContextExtra(ctx, proxyAdapter, "udp", ip, port); err == nil { - if pc, ok := wrapConn.(*wrapPacketConn); ok { - conn = pc - } else { - return nil, fmt.Errorf("conn isn't wrapPacketConn") - } - } else { - return nil, err - } - } + return p.closeClient(p.client) +} - return quic.DialEarlyContext(ctx, conn, &udpAddr, host, tlsCfg, cfg) - }, - TLSClientConfig: TLCConfig, +// closeClient cleans up resources used by client if necessary. Note, that at +// this point it should only be done for HTTP/3 as it may leak due to keep-alive +// connections. +func (p *dnsOverHTTPS) closeClient(client *http.Client) (err error) { + if isHTTP3(client) { + return client.Transport.(io.Closer).Close() + } + + return nil +} + +// exchangeHTTPS logs the request and its result and calls exchangeHTTPSClient. +func (p *dnsOverHTTPS) exchangeHTTPS(ctx context.Context, client *http.Client, req *dns.Msg) (resp *dns.Msg, err error) { + resp, err = p.exchangeHTTPSClient(ctx, client, req) + + return resp, err +} + +// exchangeHTTPSClient sends the DNS query to a DoH resolver using the specified +// http.Client instance. +func (p *dnsOverHTTPS) exchangeHTTPSClient( + ctx context.Context, + client *http.Client, + req *dns.Msg, +) (resp *dns.Msg, err error) { + buf, err := req.Pack() + if err != nil { + return nil, fmt.Errorf("packing message: %w", err) + } + + // It appears, that GET requests are more memory-efficient with Golang + // implementation of HTTP/2. + method := http.MethodGet + if isHTTP3(client) { + // If we're using HTTP/3, use http3.MethodGet0RTT to force using 0-RTT. + method = http3.MethodGet0RTT + } + + p.url.RawQuery = fmt.Sprintf("dns=%s", base64.RawURLEncoding.EncodeToString(buf)) + httpReq, err := http.NewRequest(method, p.url.String(), nil) + if err != nil { + return nil, fmt.Errorf("creating http request to %s: %w", p.url, err) + } + + httpReq.Header.Set("Accept", "application/dns-message") + httpReq.Header.Set("User-Agent", "") + _ = httpReq.WithContext(ctx) + httpResp, err := client.Do(httpReq) + if err != nil { + return nil, fmt.Errorf("requesting %s: %w", p.url, err) + } + defer httpResp.Body.Close() + + body, err := io.ReadAll(httpResp.Body) + if err != nil { + return nil, fmt.Errorf("reading %s: %w", p.url, err) + } + + if httpResp.StatusCode != http.StatusOK { + return nil, + fmt.Errorf( + "expected status %d, got %d from %s", + http.StatusOK, + httpResp.StatusCode, + p.url, + ) + } + + resp = &dns.Msg{} + err = resp.Unpack(body) + if err != nil { + return nil, fmt.Errorf( + "unpacking response from %s: body is %s: %w", + p.url, + body, + err, + ) + } + + if resp.Id != req.Id { + err = dns.ErrId + } + + return resp, err +} + +// shouldRetry checks what error we have received and returns true if we should +// re-create the HTTP client and retry the request. +func (p *dnsOverHTTPS) shouldRetry(err error) (ok bool) { + if err == nil { + return false + } + + var netErr net.Error + if errors.As(err, &netErr) && netErr.Timeout() { + // If this is a timeout error, trying to forcibly re-create the HTTP + // client instance. This is an attempt to fix an issue with DoH client + // stalling after a network change. + // + // See https://github.com/AdguardTeam/AdGuardHome/issues/3217. + return true + } + + if isQUICRetryError(err) { + return true + } + + return false +} + +// resetClient triggers re-creation of the *http.Client that is used by this +// upstream. This method accepts the error that caused resetting client as +// depending on the error we may also reset the QUIC config. +func (p *dnsOverHTTPS) resetClient(resetErr error) (client *http.Client, err error) { + p.clientMu.Lock() + defer p.clientMu.Unlock() + + if errors.Is(resetErr, quic.Err0RTTRejected) { + // Reset the TokenStore only if 0-RTT was rejected. + p.resetQUICConfig() + } + + oldClient := p.client + if oldClient != nil { + closeErr := p.closeClient(oldClient) + if closeErr != nil { + log.Warnln("warning: failed to close the old http client: %v", closeErr) + } + } + + log.Debugln("re-creating the http client due to %v", resetErr) + p.client, err = p.createClient() + + return p.client, err +} + +// getQUICConfig returns the QUIC config in a thread-safe manner. Note, that +// this method returns a pointer, it is forbidden to change its properties. +func (p *dnsOverHTTPS) getQUICConfig() (c *quic.Config) { + p.quicConfigGuard.Lock() + defer p.quicConfigGuard.Unlock() + + return p.quicConfig +} + +// resetQUICConfig Re-create the token store to make sure we're not trying to +// use invalid for 0-RTT. +func (p *dnsOverHTTPS) resetQUICConfig() { + p.quicConfigGuard.Lock() + defer p.quicConfigGuard.Unlock() + + p.quicConfig = p.quicConfig.Clone() + p.quicConfig.TokenStore = newQUICTokenStore() +} + +// getClient gets or lazily initializes an HTTP client (and transport) that will +// be used for this DoH resolver. +func (p *dnsOverHTTPS) getClient() (c *http.Client, isCached bool, err error) { + startTime := time.Now() + + p.clientMu.Lock() + defer p.clientMu.Unlock() + if p.client != nil { + return p.client, true, nil + } + + // Timeout can be exceeded while waiting for the lock. This happens quite + // often on mobile devices. + elapsed := time.Since(startTime) + if elapsed > maxElapsedTime { + return nil, false, fmt.Errorf("timeout exceeded: %s", elapsed) + } + + log.Debugln("creating a new http client") + p.client, err = p.createClient() + + return p.client, false, err +} + +// createClient creates a new *http.Client instance. The HTTP protocol version +// will depend on whether HTTP3 is allowed and provided by this upstream. Note, +// that we'll attempt to establish a QUIC connection when creating the client in +// order to check whether HTTP3 is supported. +func (p *dnsOverHTTPS) createClient() (*http.Client, error) { + transport, err := p.createTransport() + if err != nil { + return nil, fmt.Errorf("initializing http transport: %w", err) + } + + client := &http.Client{ + Transport: transport, + Timeout: DefaultTimeout, + Jar: nil, + } + + p.client = client + + return p.client, nil +} + +// createTransport initializes an HTTP transport that will be used specifically +// for this DoH resolver. This HTTP transport ensures that the HTTP requests +// will be sent exactly to the IP address got from the bootstrap resolver. Note, +// that this function will first attempt to establish a QUIC connection (if +// HTTP3 is enabled in the upstream options). If this attempt is successful, +// it returns an HTTP3 transport, otherwise it returns the H1/H2 transport. +func (p *dnsOverHTTPS) createTransport() (t http.RoundTripper, err error) { + tlsConfig := tlsC.GetGlobalFingerprintTLCConfig( + &tls.Config{ + InsecureSkipVerify: false, + MinVersion: tls.VersionTLS12, + SessionTicketsDisabled: false, + }) + var nextProtos []string + for _, v := range p.httpVersions { + nextProtos = append(nextProtos, string(v)) + } + tlsConfig.NextProtos = nextProtos + dialContext := getDialHandler(p.r, p.proxyAdapter) + // First, we attempt to create an HTTP3 transport. If the probe QUIC + // connection is established successfully, we'll be using HTTP3 for this + // upstream. + transportH3, err := p.createTransportH3(tlsConfig, dialContext) + if err == nil { + log.Debugln("using HTTP/3 for this upstream: QUIC was faster") + return transportH3, nil + } + + log.Debugln("using HTTP/2 for this upstream: %v", err) + + if !p.supportsHTTP() { + return nil, errors.New("HTTP1/1 and HTTP2 are not supported by this upstream") + } + + transport := &http.Transport{ + TLSClientConfig: tlsConfig, + DisableCompression: true, + DialContext: dialContext, + IdleConnTimeout: transportDefaultIdleConnTimeout, + MaxConnsPerHost: dohMaxConnsPerHost, + MaxIdleConns: dohMaxIdleConns, + // Since we have a custom DialContext, we need to use this field to + // make golang http.Client attempt to use HTTP/2. Otherwise, it would + // only be used when negotiated on the TLS level. + ForceAttemptHTTP2: true, + } + + // Explicitly configure transport to use HTTP/2. + // + // See https://github.com/AdguardTeam/dnsproxy/issues/11. + var transportH2 *http2.Transport + transportH2, err = http2.ConfigureTransports(transport) + if err != nil { + return nil, err + } + + // Enable HTTP/2 pings on idle connections. + transportH2.ReadIdleTimeout = transportDefaultReadIdleTimeout + + return transport, nil +} + +// http3Transport is a wrapper over *http3.RoundTripper that tries to optimize +// its behavior. The main thing that it does is trying to force use a single +// connection to a host instead of creating a new one all the time. It also +// helps mitigate race issues with quic-go. +type http3Transport struct { + baseTransport *http3.RoundTripper + + closed bool + mu sync.RWMutex +} + +// type check +var _ http.RoundTripper = (*http3Transport)(nil) + +// RoundTrip implements the http.RoundTripper interface for *http3Transport. +func (h *http3Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) { + h.mu.RLock() + defer h.mu.RUnlock() + + if h.closed { + return nil, net.ErrClosed + } + + // Try to use cached connection to the target host if it's available. + resp, err = h.baseTransport.RoundTripOpt(req, http3.RoundTripOpt{OnlyCachedConn: true}) + + if errors.Is(err, http3.ErrNoCachedConn) { + // If there are no cached connection, trigger creating a new one. + resp, err = h.baseTransport.RoundTrip(req) + } + + return resp, err +} + +// type check +var _ io.Closer = (*http3Transport)(nil) + +// Close implements the io.Closer interface for *http3Transport. +func (h *http3Transport) Close() (err error) { + h.mu.Lock() + defer h.mu.Unlock() + + h.closed = true + + return h.baseTransport.Close() +} + +// createTransportH3 tries to create an HTTP/3 transport for this upstream. +// We should be able to fall back to H1/H2 in case if HTTP/3 is unavailable or +// if it is too slow. In order to do that, this method will run two probes +// in parallel (one for TLS, the other one for QUIC) and if QUIC is faster it +// will create the *http3.RoundTripper instance. +func (doh *dnsOverHTTPS) createTransportH3( + tlsConfig *tls.Config, + dialContext dialHandler, +) (roundTripper http.RoundTripper, err error) { + if !doh.supportsH3() { + return nil, errors.New("HTTP3 support is not enabled") + } + + addr, err := doh.probeH3(tlsConfig, dialContext) + if err != nil { + return nil, err + } + + rt := &http3.RoundTripper{ + Dial: func( + ctx context.Context, + + // Ignore the address and always connect to the one that we got + // from the bootstrapper. + _ string, + tlsCfg *tls.Config, + cfg *quic.Config, + ) (c quic.EarlyConnection, err error) { + return doh.dialQuic(ctx, addr, tlsCfg, cfg) + }, + DisableCompression: true, + TLSClientConfig: tlsConfig, + QuicConfig: doh.getQUICConfig(), + } + + return &http3Transport{baseTransport: rt}, nil +} + +func (doh *dnsOverHTTPS) dialQuic(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) { + ip, port, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + portInt, err := strconv.Atoi(port) + if err != nil { + return nil, err + } + udpAddr := net.UDPAddr{ + IP: net.ParseIP(ip), + Port: portInt, + } + var conn net.PacketConn + if doh.proxyAdapter == "" { + conn, err = dialer.ListenPacket(ctx, "udp", "") + if err != nil { + return nil, err } } else { - transport = &http.Transport{ - ForceAttemptHTTP2: true, - DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - host, port, err := net.SplitHostPort(addr) - if err != nil { - return nil, err - } + if wrapConn, err := dialContextExtra(ctx, doh.proxyAdapter, "udp", udpAddr.AddrPort().Addr(), port); err == nil { + if pc, ok := wrapConn.(*wrapPacketConn); ok { + conn = pc + } else { + return nil, fmt.Errorf("conn isn't wrapPacketConn") + } + } else { + return nil, err + } + } + return quic.DialEarlyContext(ctx, conn, &udpAddr, doh.url.Host, tlsCfg, cfg) +} - ip, err := resolver.ResolveIPWithResolver(host, r) - if err != nil { - return nil, err - } +// probeH3 runs a test to check whether QUIC is faster than TLS for this +// upstream. If the test is successful it will return the address that we +// should use to establish the QUIC connections. +func (p *dnsOverHTTPS) probeH3( + tlsConfig *tls.Config, + dialContext dialHandler, +) (addr string, err error) { + // We're using bootstrapped address instead of what's passed to the function + // it does not create an actual connection, but it helps us determine + // what IP is actually reachable (when there are v4/v6 addresses). + rawConn, err := dialContext(context.Background(), "udp", p.url.Host) + if err != nil { + return "", fmt.Errorf("failed to dial: %w", err) + } + // It's never actually used. + _ = rawConn.Close() - if proxyAdapter == "" { - return dialer.DialContext(ctx, "tcp", net.JoinHostPort(ip.String(), port)) - } else { - return dialContextExtra(ctx, proxyAdapter, "tcp", ip, port) - } - }, - TLSClientConfig: TLCConfig, + udpConn, ok := rawConn.(*net.UDPConn) + if !ok { + return "", fmt.Errorf("not a UDP connection to %s", p.Address()) + } + + addr = udpConn.RemoteAddr().String() + + // Avoid spending time on probing if this upstream only supports HTTP/3. + if p.supportsH3() && !p.supportsHTTP() { + return addr, nil + } + + // Use a new *tls.Config with empty session cache for probe connections. + // Surprisingly, this is really important since otherwise it invalidates + // the existing cache. + // TODO(ameshkov): figure out why the sessions cache invalidates here. + probeTLSCfg := tlsConfig.Clone() + probeTLSCfg.ClientSessionCache = nil + + // Do not expose probe connections to the callbacks that are passed to + // the bootstrap options to avoid side-effects. + // TODO(ameshkov): consider exposing, somehow mark that this is a probe. + probeTLSCfg.VerifyPeerCertificate = nil + probeTLSCfg.VerifyConnection = nil + + // Run probeQUIC and probeTLS in parallel and see which one is faster. + chQuic := make(chan error, 1) + chTLS := make(chan error, 1) + go p.probeQUIC(addr, probeTLSCfg, chQuic) + go p.probeTLS(dialContext, probeTLSCfg, chTLS) + + select { + case quicErr := <-chQuic: + if quicErr != nil { + // QUIC failed, return error since HTTP3 was not preferred. + return "", quicErr + } + + // Return immediately, QUIC was faster. + return addr, quicErr + case tlsErr := <-chTLS: + if tlsErr != nil { + // Return immediately, TLS failed. + log.Debugln("probing TLS: %v", tlsErr) + return addr, nil + } + + return "", errors.New("TLS was faster than QUIC, prefer it") + } +} + +// probeQUIC attempts to establish a QUIC connection to the specified address. +// We run probeQUIC and probeTLS in parallel and see which one is faster. +func (p *dnsOverHTTPS) probeQUIC(addr string, tlsConfig *tls.Config, ch chan error) { + startTime := time.Now() + + timeout := DefaultTimeout + ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(timeout)) + defer cancel() + + conn, err := p.dialQuic(ctx, addr, tlsConfig, p.getQUICConfig()) + if err != nil { + ch <- fmt.Errorf("opening QUIC connection to %s: %w", p.Address(), err) + return + } + + // Ignore the error since there's no way we can use it for anything useful. + _ = conn.CloseWithError(QUICCodeNoError, "") + + ch <- nil + + elapsed := time.Now().Sub(startTime) + log.Debugln("elapsed on establishing a QUIC connection: %s", elapsed) +} + +// probeTLS attempts to establish a TLS connection to the specified address. We +// run probeQUIC and probeTLS in parallel and see which one is faster. +func (p *dnsOverHTTPS) probeTLS(dialContext dialHandler, tlsConfig *tls.Config, ch chan error) { + startTime := time.Now() + + conn, err := p.tlsDial(dialContext, "tcp", tlsConfig) + if err != nil { + ch <- fmt.Errorf("opening TLS connection: %w", err) + return + } + + // Ignore the error since there's no way we can use it for anything useful. + _ = conn.Close() + + ch <- nil + + elapsed := time.Now().Sub(startTime) + log.Debugln("elapsed on establishing a TLS connection: %s", elapsed) +} + +// supportsH3 returns true if HTTP/3 is supported by this upstream. +func (p *dnsOverHTTPS) supportsH3() (ok bool) { + for _, v := range p.supportedHTTPVersions() { + if v == C.HTTPVersion3 { + return true } } - return &dohClient{ - url: url, - transport: transport, - } + return false +} + +// supportsHTTP returns true if HTTP/1.1 or HTTP2 is supported by this upstream. +func (p *dnsOverHTTPS) supportsHTTP() (ok bool) { + for _, v := range p.supportedHTTPVersions() { + if v == C.HTTPVersion11 || v == C.HTTPVersion2 { + return true + } + } + + return false +} + +// supportedHTTPVersions returns the list of supported HTTP versions. +func (p *dnsOverHTTPS) supportedHTTPVersions() (v []C.HTTPVersion) { + v = p.httpVersions + if v == nil { + v = DefaultHTTPVersions + } + + return v +} + +// isHTTP3 checks if the *http.Client is an HTTP/3 client. +func isHTTP3(client *http.Client) (ok bool) { + _, ok = client.Transport.(*http3Transport) + + return ok +} + +// tlsDial is basically the same as tls.DialWithDialer, but we will call our own +// dialContext function to get connection. +func (doh *dnsOverHTTPS) tlsDial(dialContext dialHandler, network string, config *tls.Config) (*tls.Conn, error) { + // We're using bootstrapped address instead of what's passed + // to the function. + rawConn, err := dialContext(context.Background(), network, doh.url.Host) + if err != nil { + return nil, err + } + + // We want the timeout to cover the whole process: TCP connection and + // TLS handshake dialTimeout will be used as connection deadLine. + conn := tls.Client(rawConn, config) + + err = conn.SetDeadline(time.Now().Add(dialTimeout)) + if err != nil { + // Must not happen in normal circumstances. + panic(fmt.Errorf("cannot set deadline: %w", err)) + } + + err = conn.Handshake() + if err != nil { + defer conn.Close() + return nil, err + } + + return conn, nil } diff --git a/dns/doq.go b/dns/doq.go index 7807de1c..734d26d0 100644 --- a/dns/doq.go +++ b/dns/doq.go @@ -1,134 +1,307 @@ package dns import ( - "bytes" "context" "crypto/tls" + "encoding/binary" + "errors" "fmt" - "github.com/Dreamacro/clash/component/dialer" - "github.com/Dreamacro/clash/component/resolver" - tlsC "github.com/Dreamacro/clash/component/tls" - "github.com/lucas-clemente/quic-go" "net" + "net/netip" + "runtime" "strconv" "sync" "time" + "github.com/Dreamacro/clash/component/dialer" + tlsC "github.com/Dreamacro/clash/component/tls" + "github.com/lucas-clemente/quic-go" + "github.com/Dreamacro/clash/log" D "github.com/miekg/dns" ) const NextProtoDQ = "doq" +const ( + // QUICCodeNoError is used when the connection or stream needs to be closed, + // but there is no error to signal. + QUICCodeNoError = quic.ApplicationErrorCode(0) + // QUICCodeInternalError signals that the DoQ implementation encountered + // an internal error and is incapable of pursuing the transaction or the + // connection. + QUICCodeInternalError = quic.ApplicationErrorCode(1) + // QUICKeepAlivePeriod is the value that we pass to *quic.Config and that + // controls the period with with keep-alive frames are being sent to the + // connection. We set it to 20s as it would be in the quic-go@v0.27.1 with + // KeepAlive field set to true This value is specified in + // https://pkg.go.dev/github.com/lucas-clemente/quic-go/internal/protocol#MaxKeepAliveInterval. + // + // TODO(ameshkov): Consider making it configurable. + QUICKeepAlivePeriod = time.Second * 20 + DefaultTimeout = time.Second * 5 +) -var bytesPool = sync.Pool{New: func() interface{} { return &bytes.Buffer{} }} +type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error) + +// dnsOverQUIC is a struct that implements the Upstream interface for the +// DNS-over-QUIC protocol (spec: https://www.rfc-editor.org/rfc/rfc9250.html). +type dnsOverQUIC struct { + // quicConfig is the QUIC configuration that is used for establishing + // connections to the upstream. This configuration includes the TokenStore + // that needs to be stored for the lifetime of dnsOverQUIC since we can + // re-create the connection. + quicConfig *quic.Config + quicConfigGuard sync.Mutex + + // conn is the current active QUIC connection. It can be closed and + // re-opened when needed. + conn quic.Connection + connMu sync.RWMutex + + // bytesPool is a *sync.Pool we use to store byte buffers in. These byte + // buffers are used to read responses from the upstream. + bytesPool *sync.Pool + bytesPoolGuard sync.Mutex -type quicClient struct { addr string - r *Resolver - connection quic.Connection proxyAdapter string - udp net.PacketConn - sync.RWMutex // protects connection and bytesPool + r *Resolver } -func newDOQ(r *Resolver, addr, proxyAdapter string) *quicClient { - return &quicClient{ +// type check +var _ dnsClient = (*dnsOverQUIC)(nil) + +// newDoQ returns the DNS-over-QUIC Upstream. +func newDoQ(resolver *Resolver, addr string, adapter string) (dnsClient, error) { + doq := &dnsOverQUIC{ addr: addr, - r: r, - proxyAdapter: proxyAdapter, + proxyAdapter: adapter, + r: resolver, + quicConfig: &quic.Config{ + KeepAlivePeriod: QUICKeepAlivePeriod, + TokenStore: newQUICTokenStore(), + }, } + + runtime.SetFinalizer(doq, (*dnsOverQUIC).Close) + return doq, nil } -func (dc *quicClient) Exchange(m *D.Msg) (msg *D.Msg, err error) { - return dc.ExchangeContext(context.Background(), m) -} +// Address implements the Upstream interface for *dnsOverQUIC. +func (p *dnsOverQUIC) Address() string { return p.addr } + +func (p *dnsOverQUIC) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { + // When sending queries over a QUIC connection, the DNS Message ID MUST be + // set to zero. + id := m.Id + m.Id = 0 + defer func() { + // Restore the original ID to not break compatibility with proxies. + m.Id = id + if msg != nil { + msg.Id = id + } + }() + + // Check if there was already an active conn before sending the request. + // We'll only attempt to re-connect if there was one. + hasConnection := p.hasConnection() + + // Make the first attempt to send the DNS query. + msg, err = p.exchangeQUIC(ctx, m) + + // Make up to 2 attempts to re-open the QUIC connection and send the request + // again. There are several cases where this workaround is necessary to + // make DoQ usable. We need to make 2 attempts in the case when the + // connection was closed (due to inactivity for example) AND the server + // refuses to open a 0-RTT connection. + for i := 0; hasConnection && p.shouldRetry(err) && i < 2; i++ { + log.Debugln("re-creating the QUIC connection and retrying due to %v", err) + + // Close the active connection to make sure we'll try to re-connect. + p.closeConnWithError(err) + + // Retry sending the request. + msg, err = p.exchangeQUIC(ctx, m) + } -func (dc *quicClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { - stream, err := dc.openStream(ctx) if err != nil { - return nil, fmt.Errorf("failed to open new stream to %s", dc.addr) + // If we're unable to exchange messages, make sure the connection is + // closed and signal about an internal error. + p.closeConnWithError(err) } - buf, err := m.Pack() + return msg, err +} + +// Exchange implements the Upstream interface for *dnsOverQUIC. +func (p *dnsOverQUIC) Exchange(m *D.Msg) (msg *D.Msg, err error) { + return p.ExchangeContext(context.Background(), m) +} + +// Close implements the Upstream interface for *dnsOverQUIC. +func (p *dnsOverQUIC) Close() (err error) { + p.connMu.Lock() + defer p.connMu.Unlock() + + runtime.SetFinalizer(p, nil) + + if p.conn != nil { + err = p.conn.CloseWithError(QUICCodeNoError, "") + } + + return err +} + +// exchangeQUIC attempts to open a QUIC connection, send the DNS message +// through it and return the response it got from the server. +func (p *dnsOverQUIC) exchangeQUIC(ctx context.Context, msg *D.Msg) (resp *D.Msg, err error) { + var conn quic.Connection + conn, err = p.getConnection(true) if err != nil { return nil, err } - _, err = stream.Write(buf) + var buf []byte + buf, err = msg.Pack() + if err != nil { + return nil, fmt.Errorf("failed to pack DNS message for DoQ: %w", err) + } + + var stream quic.Stream + stream, err = p.openStream(ctx, conn) if err != nil { return nil, err } + _, err = stream.Write(AddPrefix(buf)) + if err != nil { + return nil, fmt.Errorf("failed to write to a QUIC stream: %w", err) + } + // The client MUST send the DNS query over the selected stream, and MUST // indicate through the STREAM FIN mechanism that no further data will - // be sent on that stream. - // stream.Close() -- closes the write-direction of the stream. + // be sent on that stream. Note, that stream.Close() closes the + // write-direction of the stream, but does not prevent reading from it. _ = stream.Close() - respBuf := bytesPool.Get().(*bytes.Buffer) - defer bytesPool.Put(respBuf) - defer respBuf.Reset() - - n, err := respBuf.ReadFrom(stream) - if err != nil && n == 0 { - return nil, err - } - - reply := new(D.Msg) - err = reply.Unpack(respBuf.Bytes()) - if err != nil { - return nil, err - } - - return reply, nil + return p.readMsg(stream) } -func isActive(s quic.Connection) bool { - select { - case <-s.Context().Done(): - return false - default: - return true - } +// AddPrefix adds a 2-byte prefix with the DNS message length. +func AddPrefix(b []byte) (m []byte) { + m = make([]byte, 2+len(b)) + binary.BigEndian.PutUint16(m, uint16(len(b))) + copy(m[2:], b) + + return m } -// getConnection - opens or returns an existing quic.Connection -// useCached - if true and cached connection exists, return it right away -// otherwise - forcibly creates a new connection -func (dc *quicClient) getConnection(ctx context.Context) (quic.Connection, error) { - var connection quic.Connection - dc.RLock() - connection = dc.connection +// shouldRetry checks what error we received and decides whether it is required +// to re-open the connection and retry sending the request. +func (p *dnsOverQUIC) shouldRetry(err error) (ok bool) { + return isQUICRetryError(err) +} - if connection != nil && isActive(connection) { - dc.RUnlock() - return connection, nil - } +// getBytesPool returns (creates if needed) a pool we store byte buffers in. +func (p *dnsOverQUIC) getBytesPool() (pool *sync.Pool) { + p.bytesPoolGuard.Lock() + defer p.bytesPoolGuard.Unlock() - dc.RUnlock() + if p.bytesPool == nil { + p.bytesPool = &sync.Pool{ + New: func() interface{} { + b := make([]byte, MaxMsgSize) - dc.Lock() - defer dc.Unlock() - connection = dc.connection - if connection != nil { - if isActive(connection) { - return connection, nil - } else { - _ = connection.CloseWithError(quic.ApplicationErrorCode(0), "") + return &b + }, } } - var err error - connection, err = dc.openConnection(ctx) - dc.connection = connection - return connection, err + return p.bytesPool } -func (dc *quicClient) openConnection(ctx context.Context) (quic.Connection, error) { - if dc.udp != nil { - _ = dc.udp.Close() +// getConnection opens or returns an existing quic.Connection. useCached +// argument controls whether we should try to use the existing cached +// connection. If it is false, we will forcibly create a new connection and +// close the existing one if needed. +func (p *dnsOverQUIC) getConnection(useCached bool) (quic.Connection, error) { + var conn quic.Connection + p.connMu.RLock() + conn = p.conn + if conn != nil && useCached { + p.connMu.RUnlock() + + return conn, nil + } + if conn != nil { + // we're recreating the connection, let's create a new one. + _ = conn.CloseWithError(QUICCodeNoError, "") + } + p.connMu.RUnlock() + + p.connMu.Lock() + defer p.connMu.Unlock() + + var err error + conn, err = p.openConnection() + if err != nil { + return nil, err + } + p.conn = conn + + return conn, nil +} + +// hasConnection returns true if there's an active QUIC connection. +func (p *dnsOverQUIC) hasConnection() (ok bool) { + p.connMu.Lock() + defer p.connMu.Unlock() + + return p.conn != nil +} + +// getQUICConfig returns the QUIC config in a thread-safe manner. Note, that +// this method returns a pointer, it is forbidden to change its properties. +func (p *dnsOverQUIC) getQUICConfig() (c *quic.Config) { + p.quicConfigGuard.Lock() + defer p.quicConfigGuard.Unlock() + + return p.quicConfig +} + +// resetQUICConfig re-creates the tokens store as we may need to use a new one +// if we failed to connect. +func (p *dnsOverQUIC) resetQUICConfig() { + p.quicConfigGuard.Lock() + defer p.quicConfigGuard.Unlock() + + p.quicConfig = p.quicConfig.Clone() + p.quicConfig.TokenStore = newQUICTokenStore() +} + +// openStream opens a new QUIC stream for the specified connection. +func (p *dnsOverQUIC) openStream(ctx context.Context, conn quic.Connection) (quic.Stream, error) { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + stream, err := conn.OpenStreamSync(ctx) + if err == nil { + return stream, nil } + // We can get here if the old QUIC connection is not valid anymore. We + // should try to re-create the connection again in this case. + newConn, err := p.getConnection(false) + if err != nil { + return nil, err + } + // Open a new stream. + return newConn.OpenStreamSync(ctx) +} + +// openConnection opens a new QUIC connection. +func (doq *dnsOverQUIC) openConnection() (conn quic.Connection, err error) { tlsConfig := tlsC.GetGlobalFingerprintTLCConfig( &tls.Config{ InsecureSkipVerify: false, @@ -137,42 +310,45 @@ func (dc *quicClient) openConnection(ctx context.Context) (quic.Connection, erro }, SessionTicketsDisabled: false, }) - - quicConfig := &quic.Config{ - ConnectionIDLength: 12, - HandshakeIdleTimeout: time.Second * 8, - MaxIncomingStreams: 4, - KeepAlivePeriod: 10 * time.Second, - MaxIdleTimeout: time.Second * 120, - } - - log.Debugln("opening new connection to %s", dc.addr) - var ( - udp net.PacketConn - err error - ) - - host, port, err := net.SplitHostPort(dc.addr) - + // we're using bootstrapped address instead of what's passed to the function + // it does not create an actual connection, but it helps us determine + // what IP is actually reachable (when there're v4/v6 addresses). + ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout) + rawConn, err := getDialHandler(doq.r, doq.proxyAdapter)(ctx, "udp", doq.addr) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to open a QUIC connection: %w", err) + } + // It's never actually used + _ = rawConn.Close() + cancel() + + udpConn, ok := rawConn.(*net.UDPConn) + if !ok { + return nil, fmt.Errorf("failed to open connection to %s", doq.addr) } - ip, err := resolver.ResolveIPv4WithResolver(host, dc.r) + addr := udpConn.RemoteAddr().String() + + ip, port, err := net.SplitHostPort(addr) if err != nil { return nil, err } p, err := strconv.Atoi(port) - udpAddr := net.UDPAddr{IP: ip.AsSlice(), Port: p} - - if dc.proxyAdapter == "" { + udpAddr := net.UDPAddr{IP: net.ParseIP(ip), Port: p} + var udp net.PacketConn + if doq.proxyAdapter == "" { udp, err = dialer.ListenPacket(ctx, "udp", "") if err != nil { return nil, err } } else { - conn, err := dialContextExtra(ctx, dc.proxyAdapter, "udp", ip, port) + ipAddr, err := netip.ParseAddr(ip) + if err != nil { + return nil, err + } + + conn, err := dialContextExtra(ctx, doq.proxyAdapter, "udp", ipAddr, port) if err != nil { return nil, err } @@ -185,21 +361,158 @@ func (dc *quicClient) openConnection(ctx context.Context) (quic.Connection, erro udp = wrapConn } - session, err := quic.DialContext(ctx, udp, &udpAddr, host, tlsConfig, quicConfig) - if err != nil { - return nil, fmt.Errorf("failed to open QUIC connection: %w", err) - } - - dc.udp = udp - return session, nil -} - -func (dc *quicClient) openStream(ctx context.Context) (quic.Stream, error) { - session, err := dc.getConnection(ctx) + ctx, cancel = context.WithTimeout(context.Background(), DefaultTimeout) + defer cancel() + host, _, err := net.SplitHostPort(doq.addr) if err != nil { return nil, err } - // open a new stream - return session.OpenStreamSync(ctx) + conn, err = quic.DialContext(ctx, udp, &udpAddr, host, tlsConfig, doq.getQUICConfig()) + if err != nil { + return nil, fmt.Errorf("opening quic connection to %s: %w", doq.addr, err) + } + + return conn, nil +} + +// closeConnWithError closes the active connection with error to make sure that +// new queries were processed in another connection. We can do that in the case +// of a fatal error. +func (p *dnsOverQUIC) closeConnWithError(err error) { + p.connMu.Lock() + defer p.connMu.Unlock() + + if p.conn == nil { + // Do nothing, there's no active conn anyways. + return + } + + code := QUICCodeNoError + if err != nil { + code = QUICCodeInternalError + } + + if errors.Is(err, quic.Err0RTTRejected) { + // Reset the TokenStore only if 0-RTT was rejected. + p.resetQUICConfig() + } + + err = p.conn.CloseWithError(code, "") + if err != nil { + log.Errorln("failed to close the conn: %v", err) + } + p.conn = nil +} + +// readMsg reads the incoming DNS message from the QUIC stream. +func (p *dnsOverQUIC) readMsg(stream quic.Stream) (m *D.Msg, err error) { + pool := p.getBytesPool() + bufPtr := pool.Get().(*[]byte) + + defer pool.Put(bufPtr) + + respBuf := *bufPtr + n, err := stream.Read(respBuf) + if err != nil && n == 0 { + return nil, fmt.Errorf("reading response from %s: %w", p.Address(), err) + } + + // All DNS messages (queries and responses) sent over DoQ connections MUST + // be encoded as a 2-octet length field followed by the message content as + // specified in [RFC1035]. + // IMPORTANT: Note, that we ignore this prefix here as this implementation + // does not support receiving multiple messages over a single connection. + m = new(D.Msg) + err = m.Unpack(respBuf[2:]) + if err != nil { + return nil, fmt.Errorf("unpacking response from %s: %w", p.Address(), err) + } + + return m, nil +} + +// newQUICTokenStore creates a new quic.TokenStore that is necessary to have +// in order to benefit from 0-RTT. +func newQUICTokenStore() (s quic.TokenStore) { + // You can read more on address validation here: + // https://datatracker.ietf.org/doc/html/rfc9000#section-8.1 + // Setting maxOrigins to 1 and tokensPerOrigin to 10 assuming that this is + // more than enough for the way we use it (one connection per upstream). + return quic.NewLRUTokenStore(1, 10) +} + +// isQUICRetryError checks the error and determines whether it may signal that +// we should re-create the QUIC connection. This requirement is caused by +// quic-go issues, see the comments inside this function. +// TODO(ameshkov): re-test when updating quic-go. +func isQUICRetryError(err error) (ok bool) { + var qAppErr *quic.ApplicationError + if errors.As(err, &qAppErr) && qAppErr.ErrorCode == 0 { + // This error is often returned when the server has been restarted, + // and we try to use the same connection on the client-side. It seems, + // that the old connections aren't closed immediately on the server-side + // and that's why one can run into this. + // In addition to that, quic-go HTTP3 client implementation does not + // clean up dead connections (this one is specific to DoH3 upstream): + // https://github.com/lucas-clemente/quic-go/issues/765 + return true + } + + var qIdleErr *quic.IdleTimeoutError + if errors.As(err, &qIdleErr) { + // This error means that the connection was closed due to being idle. + // In this case we should forcibly re-create the QUIC connection. + // Reproducing is rather simple, stop the server and wait for 30 seconds + // then try to send another request via the same upstream. + return true + } + + var resetErr *quic.StatelessResetError + if errors.As(err, &resetErr) { + // A stateless reset is sent when a server receives a QUIC packet that + // it doesn't know how to decrypt. For instance, it may happen when + // the server was recently rebooted. We should reconnect and try again + // in this case. + return true + } + + var qTransportError *quic.TransportError + if errors.As(err, &qTransportError) && qTransportError.ErrorCode == quic.NoError { + // A transport error with the NO_ERROR error code could be sent by the + // server when it considers that it's time to close the connection. + // For example, Google DNS eventually closes an active connection with + // the NO_ERROR code and "Connection max age expired" message: + // https://github.com/AdguardTeam/dnsproxy/issues/283 + return true + } + + if errors.Is(err, quic.Err0RTTRejected) { + // This error happens when we try to establish a 0-RTT connection with + // a token the server is no more aware of. This can be reproduced by + // restarting the QUIC server (it will clear its tokens cache). The + // next connection attempt will return this error until the client's + // tokens cache is purged. + return true + } + + return false +} + +func getDialHandler(r *Resolver, proxyAdapter string) dialHandler { + return func(ctx context.Context, network, addr string) (net.Conn, error) { + host, port, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + ip, err := r.ResolveIP(host) + if err != nil { + return nil, err + } + if len(proxyAdapter) == 0 { + return dialer.DialContext(ctx, network, net.JoinHostPort(ip.String(), port), dialer.WithDirect()) + } else { + return dialContextExtra(ctx, proxyAdapter, network, ip.Unmap(), port, dialer.WithDirect()) + } + } } diff --git a/dns/resolver.go b/dns/resolver.go index 84a38034..1184c2e7 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -355,6 +355,7 @@ type NameServer struct { Interface *atomic.String ProxyAdapter string Params map[string]string + PreferH3 bool } type FallbackFilter struct { diff --git a/dns/util.go b/dns/util.go index 50d9decd..17e4f5cf 100644 --- a/dns/util.go +++ b/dns/util.go @@ -19,6 +19,10 @@ import ( D "github.com/miekg/dns" ) +const ( + MaxMsgSize = 65535 +) + func putMsgToCache(c *cache.LruCache[string, *D.Msg], key string, msg *D.Msg) { var ttl uint32 switch { @@ -59,13 +63,17 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient { for _, s := range servers { switch s.Net { case "https": - ret = append(ret, newDoHClient(s.Addr, resolver, s.Params, s.ProxyAdapter)) + ret = append(ret, newDoHClient(s.Addr, resolver, s.PreferH3, s.Params, s.ProxyAdapter)) continue case "dhcp": ret = append(ret, newDHCPClient(s.Addr)) continue case "quic": - ret = append(ret, newDOQ(resolver, s.Addr, s.ProxyAdapter)) + if doq, err := newDoQ(resolver, s.Addr, s.ProxyAdapter); err == nil { + ret = append(ret, doq) + }else{ + log.Fatalln("DoQ format error: %v",err) + } continue } From c0bd4af12058dd568ef15bd1d85ce9316cb7aa35 Mon Sep 17 00:00:00 2001 From: Skimmle Date: Sat, 12 Nov 2022 12:12:31 +0800 Subject: [PATCH 099/250] chore: update dns config --- docs/config.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/config.yaml b/docs/config.yaml index a079a9d1..991a535f 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -107,6 +107,7 @@ profile: # DNS配置 dns: enable: false # 关闭将使用系统 DNS + perfer-h3: true # 开启 DoH 支持 HTTP/3,将并发尝试 listen: 0.0.0.0:53 # 开启 DNS 服务器监听 # ipv6: false # false 将返回 AAAA 的空结果 @@ -136,7 +137,7 @@ dns: - 8.8.8.8 # default value - tls://223.5.5.5:853 # DNS over TLS - https://doh.pub/dns-query # DNS over HTTPS - - https://dns.alidns.com/dns-query#h3=true # 强制HTTP/3 + - https://dns.alidns.com/dns-query#h3=true # 强制 HTTP/3,与 perfer-h3 无关,强制开启 DoH 的 HTTP/3 支持,若不支持将无法使用 - https://mozilla.cloudflare-dns.com/dns-query#DNS&h3=true # 指定策略组和使用 HTTP/3 - dhcp://en0 # dns from dhcp - quic://dns.adguard.com:784 # DNS over QUIC From 3321ac95ca4124e96ded940bab098712c014412f Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 12 Nov 2022 12:31:44 +0800 Subject: [PATCH 100/250] fix: cleanup import --- dns/doh.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/dns/doh.go b/dns/doh.go index d5a8b06e..157ab67b 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -21,7 +21,6 @@ import ( "github.com/Dreamacro/clash/log" "github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go/http3" - "github.com/miekg/dns" D "github.com/miekg/dns" "golang.org/x/net/http2" ) @@ -151,7 +150,7 @@ func (p *dnsOverHTTPS) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Ms } // Exchange implements the Upstream interface for *dnsOverHTTPS. -func (p *dnsOverHTTPS) Exchange(m *dns.Msg) (*dns.Msg, error) { +func (p *dnsOverHTTPS) Exchange(m *D.Msg) (*D.Msg, error) { return p.ExchangeContext(context.Background(), m) } @@ -181,7 +180,7 @@ func (p *dnsOverHTTPS) closeClient(client *http.Client) (err error) { } // exchangeHTTPS logs the request and its result and calls exchangeHTTPSClient. -func (p *dnsOverHTTPS) exchangeHTTPS(ctx context.Context, client *http.Client, req *dns.Msg) (resp *dns.Msg, err error) { +func (p *dnsOverHTTPS) exchangeHTTPS(ctx context.Context, client *http.Client, req *D.Msg) (resp *D.Msg, err error) { resp, err = p.exchangeHTTPSClient(ctx, client, req) return resp, err @@ -192,8 +191,8 @@ func (p *dnsOverHTTPS) exchangeHTTPS(ctx context.Context, client *http.Client, r func (p *dnsOverHTTPS) exchangeHTTPSClient( ctx context.Context, client *http.Client, - req *dns.Msg, -) (resp *dns.Msg, err error) { + req *D.Msg, +) (resp *D.Msg, err error) { buf, err := req.Pack() if err != nil { return nil, fmt.Errorf("packing message: %w", err) @@ -237,7 +236,7 @@ func (p *dnsOverHTTPS) exchangeHTTPSClient( ) } - resp = &dns.Msg{} + resp = &D.Msg{} err = resp.Unpack(body) if err != nil { return nil, fmt.Errorf( @@ -249,7 +248,7 @@ func (p *dnsOverHTTPS) exchangeHTTPSClient( } if resp.Id != req.Id { - err = dns.ErrId + err = D.ErrId } return resp, err From dbadf3782319a51f3c77263b9726bc9d878f05e8 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 12 Nov 2022 12:36:59 +0800 Subject: [PATCH 101/250] chore: update listeners config --- docs/config.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/config.yaml b/docs/config.yaml index 991a535f..36050e28 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -97,6 +97,12 @@ sniffer: - "443" # - 8000-9999 +# shadowsocks,vmess,tcp/udp端口转发 入口配置(传入流量将和socks,mixed等入口一样按照mode所指定的方式进行匹配处理) +# ss-config: ss://2022-blake3-aes-256-gcm:vlmpIPSyHH6f4S8WVPdRIHIlzmB+GIRfoH3aNJ/t9Gg=@:23456 +# vmess-config: vmess://1:9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68@:12345 +# tcptun-config: 127.0.0.1:801=www.example.com:80,127.0.0.1:4431=www.example.com:443 +# udptun-config: 127.0.0.1:531=8.8.8.8:53,127.0.0.1:532=1.1.1.1:53 + profile: # 存储select选择记录 store-selected: false From 901a47318d53eef71c241c1f811ecbd0fac9b3e8 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 12 Nov 2022 13:18:36 +0800 Subject: [PATCH 102/250] chore: always pass context when resolve dns --- adapter/outbound/hysteria.go | 4 +- adapter/outbound/shadowsocks.go | 2 +- adapter/outbound/shadowsocksr.go | 2 +- adapter/outbound/socks5.go | 2 +- adapter/outbound/util.go | 15 ++-- adapter/outbound/vless.go | 2 +- adapter/outbound/vmess.go | 2 +- adapter/outbound/wireguard.go | 4 +- component/dialer/dialer.go | 28 ++++---- component/resolver/local.go | 12 ++-- component/resolver/resolver.go | 114 +++++++++++++++---------------- context/dns.go | 8 ++- dns/client.go | 2 +- dns/doq.go | 4 +- dns/middleware.go | 2 +- dns/patch.go | 10 ++- dns/resolver.go | 36 +++++----- dns/server.go | 7 +- listener/sing_tun/dns.go | 13 ++-- tunnel/tunnel.go | 22 +++--- 20 files changed, 156 insertions(+), 135 deletions(-) diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index 490b14e1..4fa40f3a 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -56,7 +56,7 @@ func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata, opts . return dialer.ListenPacket(ctx, "udp", "", h.Base.DialOptions(opts...)...) }, remoteAddr: func(addr string) (net.Addr, error) { - return resolveUDPAddrWithPrefer("udp", addr, h.prefer) + return resolveUDPAddrWithPrefer(ctx, "udp", addr, h.prefer) }, } @@ -75,7 +75,7 @@ func (h *Hysteria) ListenPacketContext(ctx context.Context, metadata *C.Metadata return dialer.ListenPacket(ctx, "udp", "", h.Base.DialOptions(opts...)...) }, remoteAddr: func(addr string) (net.Addr, error) { - return resolveUDPAddrWithPrefer("udp", addr, h.prefer) + return resolveUDPAddrWithPrefer(ctx, "udp", addr, h.prefer) }, } udpConn, err := h.client.DialUDP(&hdc) diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 6eeacf45..cb0ea4fc 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -109,7 +109,7 @@ func (ss *ShadowSocks) ListenPacketContext(ctx context.Context, metadata *C.Meta return nil, err } - addr, err := resolveUDPAddrWithPrefer("udp", ss.addr, ss.prefer) + addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ss.addr, ss.prefer) if err != nil { pc.Close() return nil, err diff --git a/adapter/outbound/shadowsocksr.go b/adapter/outbound/shadowsocksr.go index 6b6b9a98..8bc9ef65 100644 --- a/adapter/outbound/shadowsocksr.go +++ b/adapter/outbound/shadowsocksr.go @@ -79,7 +79,7 @@ func (ssr *ShadowSocksR) ListenPacketContext(ctx context.Context, metadata *C.Me return nil, err } - addr, err := resolveUDPAddrWithPrefer("udp", ssr.addr, ssr.prefer) + addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ssr.addr, ssr.prefer) if err != nil { pc.Close() return nil, err diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go index 43900b1e..915e192e 100644 --- a/adapter/outbound/socks5.go +++ b/adapter/outbound/socks5.go @@ -129,7 +129,7 @@ func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata, err = errors.New("invalid UDP bind address") return } else if bindUDPAddr.IP.IsUnspecified() { - serverAddr, err := resolveUDPAddr("udp", ss.Addr()) + serverAddr, err := resolveUDPAddr(ctx, "udp", ss.Addr()) if err != nil { return nil, err } diff --git a/adapter/outbound/util.go b/adapter/outbound/util.go index 06c81868..a3d88a4e 100644 --- a/adapter/outbound/util.go +++ b/adapter/outbound/util.go @@ -2,6 +2,7 @@ package outbound import ( "bytes" + "context" "crypto/tls" xtls "github.com/xtls/go" "net" @@ -63,20 +64,20 @@ func serializesSocksAddr(metadata *C.Metadata) []byte { return bytes.Join(buf, nil) } -func resolveUDPAddr(network, address string) (*net.UDPAddr, error) { +func resolveUDPAddr(ctx context.Context, network, address string) (*net.UDPAddr, error) { host, port, err := net.SplitHostPort(address) if err != nil { return nil, err } - ip, err := resolver.ResolveProxyServerHost(host) + ip, err := resolver.ResolveProxyServerHost(ctx, host) if err != nil { return nil, err } return net.ResolveUDPAddr(network, net.JoinHostPort(ip.String(), port)) } -func resolveUDPAddrWithPrefer(network, address string, prefer C.DNSPrefer) (*net.UDPAddr, error) { +func resolveUDPAddrWithPrefer(ctx context.Context, network, address string, prefer C.DNSPrefer) (*net.UDPAddr, error) { host, port, err := net.SplitHostPort(address) if err != nil { return nil, err @@ -84,12 +85,12 @@ func resolveUDPAddrWithPrefer(network, address string, prefer C.DNSPrefer) (*net var ip netip.Addr switch prefer { case C.IPv4Only: - ip, err = resolver.ResolveIPv4ProxyServerHost(host) + ip, err = resolver.ResolveIPv4ProxyServerHost(ctx, host) case C.IPv6Only: - ip, err = resolver.ResolveIPv6ProxyServerHost(host) + ip, err = resolver.ResolveIPv6ProxyServerHost(ctx, host) case C.IPv6Prefer: var ips []netip.Addr - ips, err = resolver.ResolveAllIPProxyServerHost(host) + ips, err = resolver.LookupIPProxyServerHost(ctx, host) var fallback netip.Addr if err == nil { for _, addr := range ips { @@ -107,7 +108,7 @@ func resolveUDPAddrWithPrefer(network, address string, prefer C.DNSPrefer) (*net default: // C.IPv4Prefer, C.DualStack and other var ips []netip.Addr - ips, err = resolver.ResolveAllIPProxyServerHost(host) + ips, err = resolver.LookupIPProxyServerHost(ctx, host) var fallback netip.Addr if err == nil { for _, addr := range ips { diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index 5eb081c1..b86a9d6f 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -233,7 +233,7 @@ func (v *Vless) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { // vless use stream-oriented udp with a special address, so we needs a net.UDPAddr if !metadata.Resolved() { - ip, err := resolver.ResolveIP(metadata.Host) + ip, err := resolver.ResolveIP(ctx, metadata.Host) if err != nil { return nil, errors.New("can't resolve ip") } diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index a66ee40f..a8777bf7 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -243,7 +243,7 @@ func (v *Vmess) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { // vmess use stream-oriented udp with a special address, so we needs a net.UDPAddr if !metadata.Resolved() { - ip, err := resolver.ResolveIP(metadata.Host) + ip, err := resolver.ResolveIP(ctx, metadata.Host) if err != nil { return nil, errors.New("can't resolve ip") } diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index a6808078..eb48c0c0 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -205,7 +205,7 @@ func (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata, opts } if !metadata.Resolved() { var addrs []netip.Addr - addrs, err = resolver.ResolveAllIP(metadata.Host) + addrs, err = resolver.LookupIP(ctx, metadata.Host) if err != nil { return nil, err } @@ -229,7 +229,7 @@ func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadat return nil, err } if !metadata.Resolved() { - ip, err := resolver.ResolveIP(metadata.Host) + ip, err := resolver.ResolveIP(ctx, metadata.Host) if err != nil { return nil, errors.New("can't resolve ip") } diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index ea4d2ece..330dcf81 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -158,15 +158,15 @@ func dualStackDialContext(ctx context.Context, network, address string, opt *opt var ip netip.Addr if ipv6 { if !direct { - ip, result.error = resolver.ResolveIPv6ProxyServerHost(host) + ip, result.error = resolver.ResolveIPv6ProxyServerHost(ctx, host) } else { - ip, result.error = resolver.ResolveIPv6(host) + ip, result.error = resolver.ResolveIPv6(ctx, host) } } else { if !direct { - ip, result.error = resolver.ResolveIPv4ProxyServerHost(host) + ip, result.error = resolver.ResolveIPv4ProxyServerHost(ctx, host) } else { - ip, result.error = resolver.ResolveIPv4(host) + ip, result.error = resolver.ResolveIPv4(ctx, host) } } if result.error != nil { @@ -219,9 +219,9 @@ func concurrentDualStackDialContext(ctx context.Context, network, address string var ips []netip.Addr if opt.direct { - ips, err = resolver.ResolveAllIP(host) + ips, err = resolver.LookupIP(ctx, host) } else { - ips, err = resolver.ResolveAllIPProxyServerHost(host) + ips, err = resolver.LookupIPProxyServerHost(ctx, host) } if err != nil { @@ -344,15 +344,15 @@ func singleDialContext(ctx context.Context, network string, address string, opt switch network { case "tcp4", "udp4": if !opt.direct { - ip, err = resolver.ResolveIPv4ProxyServerHost(host) + ip, err = resolver.ResolveIPv4ProxyServerHost(ctx, host) } else { - ip, err = resolver.ResolveIPv4(host) + ip, err = resolver.ResolveIPv4(ctx, host) } default: if !opt.direct { - ip, err = resolver.ResolveIPv6ProxyServerHost(host) + ip, err = resolver.ResolveIPv6ProxyServerHost(ctx, host) } else { - ip, err = resolver.ResolveIPv6(host) + ip, err = resolver.ResolveIPv6(ctx, host) } } if err != nil { @@ -379,9 +379,9 @@ func concurrentIPv4DialContext(ctx context.Context, network, address string, opt var ips []netip.Addr if !opt.direct { - ips, err = resolver.ResolveAllIPv4ProxyServerHost(host) + ips, err = resolver.LookupIPv4ProxyServerHost(ctx, host) } else { - ips, err = resolver.ResolveAllIPv4(host) + ips, err = resolver.LookupIPv4(ctx, host) } if err != nil { @@ -399,9 +399,9 @@ func concurrentIPv6DialContext(ctx context.Context, network, address string, opt var ips []netip.Addr if !opt.direct { - ips, err = resolver.ResolveAllIPv6ProxyServerHost(host) + ips, err = resolver.LookupIPv6ProxyServerHost(ctx, host) } else { - ips, err = resolver.ResolveAllIPv6(host) + ips, err = resolver.LookupIPv6(ctx, host) } if err != nil { diff --git a/component/resolver/local.go b/component/resolver/local.go index be84e693..e8505118 100644 --- a/component/resolver/local.go +++ b/component/resolver/local.go @@ -1,17 +1,21 @@ package resolver -import D "github.com/miekg/dns" +import ( + "context" + + D "github.com/miekg/dns" +) var DefaultLocalServer LocalServer type LocalServer interface { - ServeMsg(msg *D.Msg) (*D.Msg, error) + ServeMsg(ctx context.Context, msg *D.Msg) (*D.Msg, error) } // ServeMsg with a dns.Msg, return resolve dns.Msg -func ServeMsg(msg *D.Msg) (*D.Msg, error) { +func ServeMsg(ctx context.Context, msg *D.Msg) (*D.Msg, error) { if server := DefaultLocalServer; server != nil { - return server.ServeMsg(msg) + return server.ServeMsg(ctx, msg) } return nil, ErrIPNotFound diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index 51df094f..0c09d23c 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -37,21 +37,21 @@ var ( ) type Resolver interface { - ResolveIP(host string) (ip netip.Addr, err error) - ResolveIPv4(host string) (ip netip.Addr, err error) - ResolveIPv6(host string) (ip netip.Addr, err error) - ResolveAllIP(host string) (ip []netip.Addr, err error) - ResolveAllIPv4(host string) (ips []netip.Addr, err error) - ResolveAllIPv6(host string) (ips []netip.Addr, err error) + LookupIP(ctx context.Context, host string) (ips []netip.Addr, err error) + LookupIPv4(ctx context.Context, host string) (ips []netip.Addr, err error) + LookupIPv6(ctx context.Context, host string) (ips []netip.Addr, err error) + ResolveIP(ctx context.Context, host string) (ip netip.Addr, err error) + ResolveIPv4(ctx context.Context, host string) (ip netip.Addr, err error) + ResolveIPv6(ctx context.Context, host string) (ip netip.Addr, err error) } // ResolveIPv4 with a host, return ipv4 -func ResolveIPv4(host string) (netip.Addr, error) { - return ResolveIPv4WithResolver(host, DefaultResolver) +func ResolveIPv4(ctx context.Context, host string) (netip.Addr, error) { + return ResolveIPv4WithResolver(ctx, host, DefaultResolver) } -func ResolveIPv4WithResolver(host string, r Resolver) (netip.Addr, error) { - if ips, err := ResolveAllIPv4WithResolver(host, r); err == nil { +func ResolveIPv4WithResolver(ctx context.Context, host string, r Resolver) (netip.Addr, error) { + if ips, err := LookupIPv4WithResolver(ctx, host, r); err == nil { return ips[rand.Intn(len(ips))], nil } else { return netip.Addr{}, err @@ -59,12 +59,12 @@ func ResolveIPv4WithResolver(host string, r Resolver) (netip.Addr, error) { } // ResolveIPv6 with a host, return ipv6 -func ResolveIPv6(host string) (netip.Addr, error) { - return ResolveIPv6WithResolver(host, DefaultResolver) +func ResolveIPv6(ctx context.Context, host string) (netip.Addr, error) { + return ResolveIPv6WithResolver(ctx, host, DefaultResolver) } -func ResolveIPv6WithResolver(host string, r Resolver) (netip.Addr, error) { - if ips, err := ResolveAllIPv6WithResolver(host, r); err == nil { +func ResolveIPv6WithResolver(ctx context.Context, host string, r Resolver) (netip.Addr, error) { + if ips, err := LookupIPv6WithResolver(ctx, host, r); err == nil { return ips[rand.Intn(len(ips))], nil } else { return netip.Addr{}, err @@ -72,56 +72,56 @@ func ResolveIPv6WithResolver(host string, r Resolver) (netip.Addr, error) { } // ResolveIPWithResolver same as ResolveIP, but with a resolver -func ResolveIPWithResolver(host string, r Resolver) (netip.Addr, error) { - if ip, err := ResolveIPv4WithResolver(host, r); err == nil { +func ResolveIPWithResolver(ctx context.Context, host string, r Resolver) (netip.Addr, error) { + if ip, err := ResolveIPv4WithResolver(ctx, host, r); err == nil { return ip, nil } else { - return ResolveIPv6WithResolver(host, r) + return ResolveIPv6WithResolver(ctx, host, r) } } // ResolveIP with a host, return ip -func ResolveIP(host string) (netip.Addr, error) { - return ResolveIPWithResolver(host, DefaultResolver) +func ResolveIP(ctx context.Context, host string) (netip.Addr, error) { + return ResolveIPWithResolver(ctx, host, DefaultResolver) } // ResolveIPv4ProxyServerHost proxies server host only -func ResolveIPv4ProxyServerHost(host string) (netip.Addr, error) { +func ResolveIPv4ProxyServerHost(ctx context.Context, host string) (netip.Addr, error) { if ProxyServerHostResolver != nil { - if ip, err := ResolveIPv4WithResolver(host, ProxyServerHostResolver); err != nil { - return ResolveIPv4(host) + if ip, err := ResolveIPv4WithResolver(ctx, host, ProxyServerHostResolver); err != nil { + return ResolveIPv4(ctx, host) } else { return ip, nil } } - return ResolveIPv4(host) + return ResolveIPv4(ctx, host) } // ResolveIPv6ProxyServerHost proxies server host only -func ResolveIPv6ProxyServerHost(host string) (netip.Addr, error) { +func ResolveIPv6ProxyServerHost(ctx context.Context, host string) (netip.Addr, error) { if ProxyServerHostResolver != nil { - if ip, err := ResolveIPv6WithResolver(host, ProxyServerHostResolver); err != nil { - return ResolveIPv6(host) + if ip, err := ResolveIPv6WithResolver(ctx, host, ProxyServerHostResolver); err != nil { + return ResolveIPv6(ctx, host) } else { return ip, nil } } - return ResolveIPv6(host) + return ResolveIPv6(ctx, host) } // ResolveProxyServerHost proxies server host only -func ResolveProxyServerHost(host string) (netip.Addr, error) { +func ResolveProxyServerHost(ctx context.Context, host string) (netip.Addr, error) { if ProxyServerHostResolver != nil { - if ip, err := ResolveIPWithResolver(host, ProxyServerHostResolver); err != nil { - return ResolveIP(host) + if ip, err := ResolveIPWithResolver(ctx, host, ProxyServerHostResolver); err != nil { + return ResolveIP(ctx, host) } else { return ip, err } } - return ResolveIP(host) + return ResolveIP(ctx, host) } -func ResolveAllIPv6WithResolver(host string, r Resolver) ([]netip.Addr, error) { +func LookupIPv6WithResolver(ctx context.Context, host string, r Resolver) ([]netip.Addr, error) { if DisableIPv6 { return []netip.Addr{}, ErrIPv6Disabled } @@ -141,12 +141,10 @@ func ResolveAllIPv6WithResolver(host string, r Resolver) ([]netip.Addr, error) { } if r != nil { - return r.ResolveAllIPv6(host) + return r.LookupIPv6(ctx, host) } if DefaultResolver == nil { - ctx, cancel := context.WithTimeout(context.Background(), DefaultDNSTimeout) - defer cancel() ipAddrs, err := net.DefaultResolver.LookupIP(ctx, "ip6", host) if err != nil { return []netip.Addr{}, err @@ -167,7 +165,7 @@ func ResolveAllIPv6WithResolver(host string, r Resolver) ([]netip.Addr, error) { return []netip.Addr{}, ErrIPNotFound } -func ResolveAllIPv4WithResolver(host string, r Resolver) ([]netip.Addr, error) { +func LookupIPv4WithResolver(ctx context.Context, host string, r Resolver) ([]netip.Addr, error) { if node := DefaultHosts.Search(host); node != nil { if ip := node.Data(); ip.Is4() { return []netip.Addr{node.Data()}, nil @@ -183,12 +181,10 @@ func ResolveAllIPv4WithResolver(host string, r Resolver) ([]netip.Addr, error) { } if r != nil { - return r.ResolveAllIPv4(host) + return r.LookupIPv4(ctx, host) } if DefaultResolver == nil { - ctx, cancel := context.WithTimeout(context.Background(), DefaultDNSTimeout) - defer cancel() ipAddrs, err := net.DefaultResolver.LookupIP(ctx, "ip4", host) if err != nil { return []netip.Addr{}, err @@ -209,7 +205,7 @@ func ResolveAllIPv4WithResolver(host string, r Resolver) ([]netip.Addr, error) { return []netip.Addr{}, ErrIPNotFound } -func ResolveAllIPWithResolver(host string, r Resolver) ([]netip.Addr, error) { +func LookupIPWithResolver(ctx context.Context, host string, r Resolver) ([]netip.Addr, error) { if node := DefaultHosts.Search(host); node != nil { return []netip.Addr{node.Data()}, nil } @@ -221,16 +217,16 @@ func ResolveAllIPWithResolver(host string, r Resolver) ([]netip.Addr, error) { if r != nil { if DisableIPv6 { - return r.ResolveAllIPv4(host) + return r.LookupIPv4(ctx, host) } - return r.ResolveAllIP(host) + return r.LookupIP(ctx, host) } else if DisableIPv6 { - return ResolveAllIPv4(host) + return LookupIPv4(ctx, host) } if DefaultResolver == nil { - ipAddrs, err := net.DefaultResolver.LookupIP(context.Background(), "ip", host) + ipAddrs, err := net.DefaultResolver.LookupIP(ctx, "ip", host) if err != nil { return []netip.Addr{}, err } else if len(ipAddrs) == 0 { @@ -249,35 +245,35 @@ func ResolveAllIPWithResolver(host string, r Resolver) ([]netip.Addr, error) { return []netip.Addr{}, ErrIPNotFound } -func ResolveAllIP(host string) ([]netip.Addr, error) { - return ResolveAllIPWithResolver(host, DefaultResolver) +func LookupIP(ctx context.Context, host string) ([]netip.Addr, error) { + return LookupIPWithResolver(ctx, host, DefaultResolver) } -func ResolveAllIPv4(host string) ([]netip.Addr, error) { - return ResolveAllIPv4WithResolver(host, DefaultResolver) +func LookupIPv4(ctx context.Context, host string) ([]netip.Addr, error) { + return LookupIPv4WithResolver(ctx, host, DefaultResolver) } -func ResolveAllIPv6(host string) ([]netip.Addr, error) { - return ResolveAllIPv6WithResolver(host, DefaultResolver) +func LookupIPv6(ctx context.Context, host string) ([]netip.Addr, error) { + return LookupIPv6WithResolver(ctx, host, DefaultResolver) } -func ResolveAllIPv6ProxyServerHost(host string) ([]netip.Addr, error) { +func LookupIPv6ProxyServerHost(ctx context.Context, host string) ([]netip.Addr, error) { if ProxyServerHostResolver != nil { - return ResolveAllIPv6WithResolver(host, ProxyServerHostResolver) + return LookupIPv6WithResolver(ctx, host, ProxyServerHostResolver) } - return ResolveAllIPv6(host) + return LookupIPv6(ctx, host) } -func ResolveAllIPv4ProxyServerHost(host string) ([]netip.Addr, error) { +func LookupIPv4ProxyServerHost(ctx context.Context, host string) ([]netip.Addr, error) { if ProxyServerHostResolver != nil { - return ResolveAllIPv4WithResolver(host, ProxyServerHostResolver) + return LookupIPv4WithResolver(ctx, host, ProxyServerHostResolver) } - return ResolveAllIPv4(host) + return LookupIPv4(ctx, host) } -func ResolveAllIPProxyServerHost(host string) ([]netip.Addr, error) { +func LookupIPProxyServerHost(ctx context.Context, host string) ([]netip.Addr, error) { if ProxyServerHostResolver != nil { - return ResolveAllIPWithResolver(host, ProxyServerHostResolver) + return LookupIPWithResolver(ctx, host, ProxyServerHostResolver) } - return ResolveAllIP(host) + return LookupIP(ctx, host) } diff --git a/context/dns.go b/context/dns.go index 0be4a1fc..59130961 100644 --- a/context/dns.go +++ b/context/dns.go @@ -1,6 +1,8 @@ package context import ( + "context" + "github.com/gofrs/uuid" "github.com/miekg/dns" ) @@ -12,14 +14,18 @@ const ( ) type DNSContext struct { + context.Context + id uuid.UUID msg *dns.Msg tp string } -func NewDNSContext(msg *dns.Msg) *DNSContext { +func NewDNSContext(ctx context.Context, msg *dns.Msg) *DNSContext { id, _ := uuid.NewV4() return &DNSContext{ + Context: ctx, + id: id, msg: msg, } diff --git a/dns/client.go b/dns/client.go index a377ee42..13b01422 100644 --- a/dns/client.go +++ b/dns/client.go @@ -38,7 +38,7 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) if c.r == nil { return nil, fmt.Errorf("dns %s not a valid ip", c.host) } else { - if ip, err = resolver.ResolveIPWithResolver(c.host, c.r); err != nil { + if ip, err = resolver.ResolveIPWithResolver(ctx, c.host, c.r); err != nil { return nil, fmt.Errorf("use default dns resolve failed: %w", err) } c.host = ip.String() diff --git a/dns/doq.go b/dns/doq.go index 734d26d0..07cceff6 100644 --- a/dns/doq.go +++ b/dns/doq.go @@ -347,7 +347,7 @@ func (doq *dnsOverQUIC) openConnection() (conn quic.Connection, err error) { if err != nil { return nil, err } - + conn, err := dialContextExtra(ctx, doq.proxyAdapter, "udp", ipAddr, port) if err != nil { return nil, err @@ -505,7 +505,7 @@ func getDialHandler(r *Resolver, proxyAdapter string) dialHandler { if err != nil { return nil, err } - ip, err := r.ResolveIP(host) + ip, err := r.ResolveIP(ctx, host) if err != nil { return nil, err } diff --git a/dns/middleware.go b/dns/middleware.go index 0e1335f9..28ced849 100644 --- a/dns/middleware.go +++ b/dns/middleware.go @@ -156,7 +156,7 @@ func withResolver(resolver *Resolver) handler { return handleMsgWithEmptyAnswer(r), nil } - msg, err := resolver.Exchange(r) + msg, err := resolver.ExchangeContext(ctx, r) if err != nil { log.Debugln("[DNS Server] Exchange %s failed: %v", q.String(), err) return msg, err diff --git a/dns/patch.go b/dns/patch.go index 76974243..37b5d41b 100644 --- a/dns/patch.go +++ b/dns/patch.go @@ -1,14 +1,18 @@ package dns -import D "github.com/miekg/dns" +import ( + "context" + + D "github.com/miekg/dns" +) type LocalServer struct { handler handler } // ServeMsg implement resolver.LocalServer ResolveMsg -func (s *LocalServer) ServeMsg(msg *D.Msg) (*D.Msg, error) { - return handlerWithContext(s.handler, msg) +func (s *LocalServer) ServeMsg(ctx context.Context, msg *D.Msg) (*D.Msg, error) { + return handlerWithContext(ctx, s.handler, msg) } func NewLocalServer(resolver *Resolver, mapper *ResolverEnhancer) *LocalServer { diff --git a/dns/resolver.go b/dns/resolver.go index 1184c2e7..eacb7dc4 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -44,18 +44,18 @@ type Resolver struct { proxyServer []dnsClient } -func (r *Resolver) ResolveAllIPPrimaryIPv4(host string) (ips []netip.Addr, err error) { +func (r *Resolver) LookupIPPrimaryIPv4(ctx context.Context, host string) (ips []netip.Addr, err error) { ch := make(chan []netip.Addr, 1) go func() { defer close(ch) - ip, err := r.resolveIP(host, D.TypeAAAA) + ip, err := r.resolveIP(ctx, host, D.TypeAAAA) if err != nil { return } ch <- ip }() - ips, err = r.resolveIP(host, D.TypeA) + ips, err = r.resolveIP(ctx, host, D.TypeA) if err == nil { return } @@ -68,11 +68,11 @@ func (r *Resolver) ResolveAllIPPrimaryIPv4(host string) (ips []netip.Addr, err e return ip, nil } -func (r *Resolver) ResolveAllIP(host string) (ips []netip.Addr, err error) { +func (r *Resolver) LookupIP(ctx context.Context, host string) (ips []netip.Addr, err error) { ch := make(chan []netip.Addr, 1) go func() { defer close(ch) - ip, err := r.resolveIP(host, D.TypeAAAA) + ip, err := r.resolveIP(ctx, host, D.TypeAAAA) if err != nil { return } @@ -80,7 +80,7 @@ func (r *Resolver) ResolveAllIP(host string) (ips []netip.Addr, err error) { ch <- ip }() - ips, err = r.resolveIP(host, D.TypeA) + ips, err = r.resolveIP(ctx, host, D.TypeA) select { case ipv6s, open := <-ch: @@ -95,17 +95,17 @@ func (r *Resolver) ResolveAllIP(host string) (ips []netip.Addr, err error) { return ips, nil } -func (r *Resolver) ResolveAllIPv4(host string) (ips []netip.Addr, err error) { - return r.resolveIP(host, D.TypeA) +func (r *Resolver) LookupIPv4(ctx context.Context, host string) (ips []netip.Addr, err error) { + return r.resolveIP(ctx, host, D.TypeA) } -func (r *Resolver) ResolveAllIPv6(host string) (ips []netip.Addr, err error) { - return r.resolveIP(host, D.TypeAAAA) +func (r *Resolver) LookupIPv6(ctx context.Context, host string) (ips []netip.Addr, err error) { + return r.resolveIP(ctx, host, D.TypeAAAA) } // ResolveIP request with TypeA and TypeAAAA, priority return TypeA -func (r *Resolver) ResolveIP(host string) (ip netip.Addr, err error) { - if ips, err := r.ResolveAllIPPrimaryIPv4(host); err == nil { +func (r *Resolver) ResolveIP(ctx context.Context, host string) (ip netip.Addr, err error) { + if ips, err := r.LookupIPPrimaryIPv4(ctx, host); err == nil { return ips[rand.Intn(len(ips))], nil } else { return netip.Addr{}, err @@ -113,8 +113,8 @@ func (r *Resolver) ResolveIP(host string) (ip netip.Addr, err error) { } // ResolveIPv4 request with TypeA -func (r *Resolver) ResolveIPv4(host string) (ip netip.Addr, err error) { - if ips, err := r.ResolveAllIPv4(host); err == nil { +func (r *Resolver) ResolveIPv4(ctx context.Context, host string) (ip netip.Addr, err error) { + if ips, err := r.LookupIPv4(ctx, host); err == nil { return ips[rand.Intn(len(ips))], nil } else { return netip.Addr{}, err @@ -122,8 +122,8 @@ func (r *Resolver) ResolveIPv4(host string) (ip netip.Addr, err error) { } // ResolveIPv6 request with TypeAAAA -func (r *Resolver) ResolveIPv6(host string) (ip netip.Addr, err error) { - if ips, err := r.ResolveAllIPv6(host); err == nil { +func (r *Resolver) ResolveIPv6(ctx context.Context, host string) (ip netip.Addr, err error) { + if ips, err := r.LookupIPv6(ctx, host); err == nil { return ips[rand.Intn(len(ips))], nil } else { return netip.Addr{}, err @@ -305,7 +305,7 @@ func (r *Resolver) ipExchange(ctx context.Context, m *D.Msg) (msg *D.Msg, err er return } -func (r *Resolver) resolveIP(host string, dnsType uint16) (ips []netip.Addr, err error) { +func (r *Resolver) resolveIP(ctx context.Context, host string, dnsType uint16) (ips []netip.Addr, err error) { ip, err := netip.ParseAddr(host) if err == nil { isIPv4 := ip.Is4() @@ -321,7 +321,7 @@ func (r *Resolver) resolveIP(host string, dnsType uint16) (ips []netip.Addr, err query := &D.Msg{} query.SetQuestion(D.Fqdn(host), dnsType) - msg, err := r.Exchange(query) + msg, err := r.ExchangeContext(ctx, query) if err != nil { return []netip.Addr{}, err } diff --git a/dns/server.go b/dns/server.go index 1fbde824..5c5970db 100644 --- a/dns/server.go +++ b/dns/server.go @@ -1,6 +1,7 @@ package dns import ( + stdContext "context" "errors" "net" @@ -25,7 +26,7 @@ type Server struct { // ServeDNS implement D.Handler ServeDNS func (s *Server) ServeDNS(w D.ResponseWriter, r *D.Msg) { - msg, err := handlerWithContext(s.handler, r) + msg, err := handlerWithContext(stdContext.Background(), s.handler, r) if err != nil { D.HandleFailed(w, r) return @@ -34,12 +35,12 @@ func (s *Server) ServeDNS(w D.ResponseWriter, r *D.Msg) { w.WriteMsg(msg) } -func handlerWithContext(handler handler, msg *D.Msg) (*D.Msg, error) { +func handlerWithContext(stdCtx stdContext.Context, handler handler, msg *D.Msg) (*D.Msg, error) { if len(msg.Question) == 0 { return nil, errors.New("at least one question is required") } - ctx := context.NewDNSContext(msg) + ctx := context.NewDNSContext(stdCtx, msg) return handler(ctx, msg) } diff --git a/listener/sing_tun/dns.go b/listener/sing_tun/dns.go index 39e2b1e5..21dee43c 100644 --- a/listener/sing_tun/dns.go +++ b/listener/sing_tun/dns.go @@ -23,6 +23,7 @@ import ( ) const DefaultDnsReadTimeout = time.Second * 10 +const DefaultDnsRelayTimeout = time.Second * 5 type ListenerHandler struct { sing.ListenerHandler @@ -69,8 +70,10 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta } err = func() error { + ctx, cancel := context.WithTimeout(ctx, DefaultDnsRelayTimeout) + defer cancel() inData := buff[:n] - msg, err := RelayDnsPacket(inData) + msg, err := RelayDnsPacket(ctx, inData) if err != nil { return err } @@ -117,8 +120,10 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network. return err } go func() { + ctx, cancel := context.WithTimeout(ctx, DefaultDnsRelayTimeout) + defer cancel() inData := buff.Bytes() - msg, err := RelayDnsPacket(inData) + msg, err := RelayDnsPacket(ctx, inData) if err != nil { buff.Release() return @@ -146,13 +151,13 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network. return h.ListenerHandler.NewPacketConnection(ctx, conn, metadata) } -func RelayDnsPacket(payload []byte) ([]byte, error) { +func RelayDnsPacket(ctx context.Context, payload []byte) ([]byte, error) { msg := &D.Msg{} if err := msg.Unpack(payload); err != nil { return nil, err } - r, err := resolver.ServeMsg(msg) + r, err := resolver.ServeMsg(ctx, msg) if err != nil { m := new(D.Msg) m.SetRcode(msg, D.RcodeServerFailure) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 46b603a3..bdac6cce 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -225,7 +225,7 @@ func handleUDPConn(packet *inbound.PacketAdapter) { // local resolve UDP dns if !metadata.Resolved() { - ip, err := resolver.ResolveIP(metadata.Host) + ip, err := resolver.ResolveIP(context.Background(), metadata.Host) if err != nil { return } @@ -400,14 +400,18 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { for _, rule := range rules { if !resolved && shouldResolveIP(rule, metadata) { - ip, err := resolver.ResolveIP(metadata.Host) - if err != nil { - log.Debugln("[DNS] resolve %s error: %s", metadata.Host, err.Error()) - } else { - log.Debugln("[DNS] %s --> %s", metadata.Host, ip.String()) - metadata.DstIP = ip - } - resolved = true + func() { + ctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout) + defer cancel() + ip, err := resolver.ResolveIP(ctx, metadata.Host) + if err != nil { + log.Debugln("[DNS] resolve %s error: %s", metadata.Host, err.Error()) + } else { + log.Debugln("[DNS] %s --> %s", metadata.Host, ip.String()) + metadata.DstIP = ip + } + resolved = true + }() } if !processFound && (alwaysFindProcess || rule.ShouldFindProcess()) { From 75d339392bf204423a4313bd16f1a7448f6ce652 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 12 Nov 2022 18:29:19 +0800 Subject: [PATCH 103/250] chore: better dns background fetch retrying --- dns/resolver.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/dns/resolver.go b/dns/resolver.go index eacb7dc4..101819b7 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -149,6 +149,16 @@ func (r *Resolver) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, e if len(m.Question) == 0 { return nil, errors.New("should have one question at least") } + continueFetch := false + defer func() { + if continueFetch || errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) { + ctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout) + defer cancel() + go func() { + _, _ = r.exchangeWithoutCache(ctx, m) // ignore result, just for putMsgToCache + }() + } + }() q := m.Question[0] cacheM, expireTime, hit := r.lruCache.GetWithExpire(q.String()) @@ -157,7 +167,7 @@ func (r *Resolver) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, e msg = cacheM.Copy() if expireTime.Before(now) { setMsgTTL(msg, uint32(1)) // Continue fetch - go r.exchangeWithoutCache(ctx, m) + continueFetch = true } else { setMsgTTL(msg, uint32(time.Until(expireTime).Seconds())) } From 7300c917dc19a47fb9a5d79ce35ea9cf93b3946e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 12 Nov 2022 20:59:29 +0800 Subject: [PATCH 104/250] fix: build error --- component/process/process_freebsd_amd64.go | 6 +++--- component/process/process_other.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/component/process/process_freebsd_amd64.go b/component/process/process_freebsd_amd64.go index ab8bcf69..ffbe1515 100644 --- a/component/process/process_freebsd_amd64.go +++ b/component/process/process_freebsd_amd64.go @@ -51,17 +51,17 @@ func findProcessName(network string, ip netip.Addr, srcPort int) (*uint32, strin value, err := syscall.Sysctl(spath) if err != nil { - return -1, "", err + return nil, "", err } buf := []byte(value) pid, err := defaultSearcher.Search(buf, ip, uint16(srcPort), isTCP) if err != nil { - return -1, "", err + return nil, "", err } pp, err := getExecPathFromPID(pid) - return -1, pp, err + return nil, pp, err } func getExecPathFromPID(pid uint32) (string, error) { diff --git a/component/process/process_other.go b/component/process/process_other.go index e5cc93cc..32614b26 100644 --- a/component/process/process_other.go +++ b/component/process/process_other.go @@ -5,7 +5,7 @@ package process import "net/netip" func findProcessName(network string, ip netip.Addr, srcPort int) (*uint32, string, error) { - return -1, "", ErrPlatformNotSupport + return nil, "", ErrPlatformNotSupport } func resolveSocketByNetlink(network string, ip netip.Addr, srcPort int) (uint32, uint32, error) { From a3425c0e789f7b607e71bd790c9e477d0f3e8c4d Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 12 Nov 2022 21:31:07 +0800 Subject: [PATCH 105/250] chore: sync dns code --- component/resolver/resolver.go | 294 ++++++++++++++++----------------- dns/resolver.go | 43 +++-- 2 files changed, 168 insertions(+), 169 deletions(-) diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index 0c09d23c..dfe47000 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -3,12 +3,13 @@ package resolver import ( "context" "errors" + "fmt" "math/rand" "net" "net/netip" + "strings" "time" - "github.com/Dreamacro/clash/common/nnip" "github.com/Dreamacro/clash/component/trie" ) @@ -45,39 +46,164 @@ type Resolver interface { ResolveIPv6(ctx context.Context, host string) (ip netip.Addr, err error) } +// LookupIPv4WithResolver same as LookupIPv4, but with a resolver +func LookupIPv4WithResolver(ctx context.Context, host string, r Resolver) ([]netip.Addr, error) { + if node := DefaultHosts.Search(host); node != nil { + if ip := node.Data(); ip.Is4() { + return []netip.Addr{node.Data()}, nil + } + } + + ip, err := netip.ParseAddr(host) + if err == nil { + if ip.Is4() || ip.Is4In6() { + return []netip.Addr{ip}, nil + } + return []netip.Addr{}, ErrIPVersion + } + + if r != nil { + return r.LookupIPv4(ctx, host) + } + + if DefaultResolver != nil { + return DefaultResolver.LookupIPv4(ctx, host) + } + + ctx, cancel := context.WithTimeout(context.Background(), DefaultDNSTimeout) + defer cancel() + ipAddrs, err := net.DefaultResolver.LookupNetIP(ctx, "ip4", host) + if err != nil { + return nil, err + } else if len(ipAddrs) == 0 { + return nil, ErrIPNotFound + } + + return ipAddrs, nil +} + +// LookupIPv4 with a host, return ipv4 list +func LookupIPv4(ctx context.Context, host string) ([]netip.Addr, error) { + return LookupIPv4WithResolver(ctx, host, DefaultResolver) +} + +// ResolveIPv4WithResolver same as ResolveIPv4, but with a resolver +func ResolveIPv4WithResolver(ctx context.Context, host string, r Resolver) (netip.Addr, error) { + ips, err := LookupIPv4WithResolver(ctx, host, r) + if err != nil { + return netip.Addr{}, err + } else if len(ips) == 0 { + return netip.Addr{}, fmt.Errorf("%w: %s", ErrIPNotFound, host) + } + return ips[rand.Intn(len(ips))], nil +} + // ResolveIPv4 with a host, return ipv4 func ResolveIPv4(ctx context.Context, host string) (netip.Addr, error) { return ResolveIPv4WithResolver(ctx, host, DefaultResolver) } -func ResolveIPv4WithResolver(ctx context.Context, host string, r Resolver) (netip.Addr, error) { - if ips, err := LookupIPv4WithResolver(ctx, host, r); err == nil { - return ips[rand.Intn(len(ips))], nil - } else { - return netip.Addr{}, err +// LookupIPv6WithResolver same as LookupIPv6, but with a resolver +func LookupIPv6WithResolver(ctx context.Context, host string, r Resolver) ([]netip.Addr, error) { + if DisableIPv6 { + return nil, ErrIPv6Disabled } + + if node := DefaultHosts.Search(host); node != nil { + if ip := node.Data(); ip.Is6() { + return []netip.Addr{ip}, nil + } + } + + if ip, err := netip.ParseAddr(host); err == nil { + if strings.Contains(host, ":") { + return []netip.Addr{ip}, nil + } + return nil, ErrIPVersion + } + + if r != nil { + return r.LookupIPv6(ctx, host) + } + if DefaultResolver != nil { + return DefaultResolver.LookupIPv6(ctx, host) + } + + ctx, cancel := context.WithTimeout(context.Background(), DefaultDNSTimeout) + defer cancel() + ipAddrs, err := net.DefaultResolver.LookupNetIP(ctx, "ip6", host) + if err != nil { + return nil, err + } else if len(ipAddrs) == 0 { + return nil, ErrIPNotFound + } + + return ipAddrs, nil +} + +// LookupIPv6 with a host, return ipv6 list +func LookupIPv6(ctx context.Context, host string) ([]netip.Addr, error) { + return LookupIPv6WithResolver(ctx, host, DefaultResolver) +} + +// ResolveIPv6WithResolver same as ResolveIPv6, but with a resolver +func ResolveIPv6WithResolver(ctx context.Context, host string, r Resolver) (netip.Addr, error) { + ips, err := LookupIPv6WithResolver(ctx, host, r) + if err != nil { + return netip.Addr{}, err + } else if len(ips) == 0 { + return netip.Addr{}, fmt.Errorf("%w: %s", ErrIPNotFound, host) + } + return ips[rand.Intn(len(ips))], nil } -// ResolveIPv6 with a host, return ipv6 func ResolveIPv6(ctx context.Context, host string) (netip.Addr, error) { return ResolveIPv6WithResolver(ctx, host, DefaultResolver) } -func ResolveIPv6WithResolver(ctx context.Context, host string, r Resolver) (netip.Addr, error) { - if ips, err := LookupIPv6WithResolver(ctx, host, r); err == nil { - return ips[rand.Intn(len(ips))], nil - } else { - return netip.Addr{}, err +// LookupIPWithResolver same as LookupIP, but with a resolver +func LookupIPWithResolver(ctx context.Context, host string, r Resolver) ([]netip.Addr, error) { + if node := DefaultHosts.Search(host); node != nil { + return []netip.Addr{node.Data()}, nil } + + if r != nil { + if DisableIPv6 { + return r.LookupIPv4(ctx, host) + } + return r.LookupIP(ctx, host) + } else if DisableIPv6 { + return LookupIPv4(ctx, host) + } + + if ip, err := netip.ParseAddr(host); err == nil { + return []netip.Addr{ip}, nil + } + + ips, err := net.DefaultResolver.LookupNetIP(ctx, "ip", host) + if err != nil { + return nil, err + } else if len(ips) == 0 { + return nil, ErrIPNotFound + } + + return ips, nil +} + +// LookupIP with a host, return ip +func LookupIP(ctx context.Context, host string) ([]netip.Addr, error) { + return LookupIPWithResolver(ctx, host, DefaultResolver) } // ResolveIPWithResolver same as ResolveIP, but with a resolver func ResolveIPWithResolver(ctx context.Context, host string, r Resolver) (netip.Addr, error) { - if ip, err := ResolveIPv4WithResolver(ctx, host, r); err == nil { - return ip, nil - } else { - return ResolveIPv6WithResolver(ctx, host, r) + ips, err := LookupIPWithResolver(ctx, host, r) + if err != nil { + return netip.Addr{}, err + } else if len(ips) == 0 { + return netip.Addr{}, fmt.Errorf("%w: %s", ErrIPNotFound, host) } + return ips[rand.Intn(len(ips))], nil } // ResolveIP with a host, return ip @@ -121,142 +247,6 @@ func ResolveProxyServerHost(ctx context.Context, host string) (netip.Addr, error return ResolveIP(ctx, host) } -func LookupIPv6WithResolver(ctx context.Context, host string, r Resolver) ([]netip.Addr, error) { - if DisableIPv6 { - return []netip.Addr{}, ErrIPv6Disabled - } - - if node := DefaultHosts.Search(host); node != nil { - if ip := node.Data(); ip.Is6() { - return []netip.Addr{ip}, nil - } - } - - ip, err := netip.ParseAddr(host) - if err == nil { - if ip.Is6() { - return []netip.Addr{ip}, nil - } - return []netip.Addr{}, ErrIPVersion - } - - if r != nil { - return r.LookupIPv6(ctx, host) - } - - if DefaultResolver == nil { - ipAddrs, err := net.DefaultResolver.LookupIP(ctx, "ip6", host) - if err != nil { - return []netip.Addr{}, err - } else if len(ipAddrs) == 0 { - return []netip.Addr{}, ErrIPNotFound - } - - addrs := make([]netip.Addr, 0, len(ipAddrs)) - for _, ipAddr := range ipAddrs { - addrs = append(addrs, nnip.IpToAddr(ipAddr)) - } - - rand.Shuffle(len(addrs), func(i, j int) { - addrs[i], addrs[j] = addrs[j], addrs[i] - }) - return addrs, nil - } - return []netip.Addr{}, ErrIPNotFound -} - -func LookupIPv4WithResolver(ctx context.Context, host string, r Resolver) ([]netip.Addr, error) { - if node := DefaultHosts.Search(host); node != nil { - if ip := node.Data(); ip.Is4() { - return []netip.Addr{node.Data()}, nil - } - } - - ip, err := netip.ParseAddr(host) - if err == nil { - if ip.Is4() || ip.Is4In6() { - return []netip.Addr{ip}, nil - } - return []netip.Addr{}, ErrIPVersion - } - - if r != nil { - return r.LookupIPv4(ctx, host) - } - - if DefaultResolver == nil { - ipAddrs, err := net.DefaultResolver.LookupIP(ctx, "ip4", host) - if err != nil { - return []netip.Addr{}, err - } else if len(ipAddrs) == 0 { - return []netip.Addr{}, ErrIPNotFound - } - - addrs := make([]netip.Addr, 0, len(ipAddrs)) - for _, ipAddr := range ipAddrs { - addrs = append(addrs, nnip.IpToAddr(ipAddr)) - } - - rand.Shuffle(len(addrs), func(i, j int) { - addrs[i], addrs[j] = addrs[j], addrs[i] - }) - return addrs, nil - } - return []netip.Addr{}, ErrIPNotFound -} - -func LookupIPWithResolver(ctx context.Context, host string, r Resolver) ([]netip.Addr, error) { - if node := DefaultHosts.Search(host); node != nil { - return []netip.Addr{node.Data()}, nil - } - - ip, err := netip.ParseAddr(host) - if err == nil { - return []netip.Addr{ip}, nil - } - - if r != nil { - if DisableIPv6 { - return r.LookupIPv4(ctx, host) - } - - return r.LookupIP(ctx, host) - } else if DisableIPv6 { - return LookupIPv4(ctx, host) - } - - if DefaultResolver == nil { - ipAddrs, err := net.DefaultResolver.LookupIP(ctx, "ip", host) - if err != nil { - return []netip.Addr{}, err - } else if len(ipAddrs) == 0 { - return []netip.Addr{}, ErrIPNotFound - } - addrs := make([]netip.Addr, 0, len(ipAddrs)) - for _, ipAddr := range ipAddrs { - addrs = append(addrs, nnip.IpToAddr(ipAddr)) - } - - rand.Shuffle(len(addrs), func(i, j int) { - addrs[i], addrs[j] = addrs[j], addrs[i] - }) - return addrs, nil - } - return []netip.Addr{}, ErrIPNotFound -} - -func LookupIP(ctx context.Context, host string) ([]netip.Addr, error) { - return LookupIPWithResolver(ctx, host, DefaultResolver) -} - -func LookupIPv4(ctx context.Context, host string) ([]netip.Addr, error) { - return LookupIPv4WithResolver(ctx, host, DefaultResolver) -} - -func LookupIPv6(ctx context.Context, host string) ([]netip.Addr, error) { - return LookupIPv6WithResolver(ctx, host, DefaultResolver) -} - func LookupIPv6ProxyServerHost(ctx context.Context, host string) ([]netip.Addr, error) { if ProxyServerHostResolver != nil { return LookupIPv6WithResolver(ctx, host, ProxyServerHostResolver) diff --git a/dns/resolver.go b/dns/resolver.go index a8e9c2d9..a0623dc2 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -3,6 +3,7 @@ package dns import ( "context" "errors" + "fmt" "go.uber.org/atomic" "math/rand" "net/netip" @@ -93,39 +94,47 @@ func (r *Resolver) LookupIP(ctx context.Context, host string) (ips []netip.Addr, return ips, nil } -func (r *Resolver) LookupIPv4(ctx context.Context, host string) (ips []netip.Addr, err error) { - return r.lookupIP(ctx, host, D.TypeA) -} - -func (r *Resolver) LookupIPv6(ctx context.Context, host string) (ips []netip.Addr, err error) { - return r.lookupIP(ctx, host, D.TypeAAAA) -} - // ResolveIP request with TypeA and TypeAAAA, priority return TypeA func (r *Resolver) ResolveIP(ctx context.Context, host string) (ip netip.Addr, err error) { - if ips, err := r.LookupIPPrimaryIPv4(ctx, host); err == nil { - return ips[rand.Intn(len(ips))], nil - } else { + ips, err := r.LookupIPPrimaryIPv4(ctx, host) + if err != nil { return netip.Addr{}, err + } else if len(ips) == 0 { + return netip.Addr{}, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, host) } + return ips[rand.Intn(len(ips))], nil +} + +// LookupIPv4 request with TypeA +func (r *Resolver) LookupIPv4(ctx context.Context, host string) ([]netip.Addr, error) { + return r.lookupIP(ctx, host, D.TypeA) } // ResolveIPv4 request with TypeA func (r *Resolver) ResolveIPv4(ctx context.Context, host string) (ip netip.Addr, err error) { - if ips, err := r.LookupIPv4(ctx, host); err == nil { - return ips[rand.Intn(len(ips))], nil - } else { + ips, err := r.lookupIP(ctx, host, D.TypeA) + if err != nil { return netip.Addr{}, err + } else if len(ips) == 0 { + return netip.Addr{}, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, host) } + return ips[rand.Intn(len(ips))], nil +} + +// LookupIPv6 request with TypeAAAA +func (r *Resolver) LookupIPv6(ctx context.Context, host string) ([]netip.Addr, error) { + return r.lookupIP(ctx, host, D.TypeAAAA) } // ResolveIPv6 request with TypeAAAA func (r *Resolver) ResolveIPv6(ctx context.Context, host string) (ip netip.Addr, err error) { - if ips, err := r.LookupIPv6(ctx, host); err == nil { - return ips[rand.Intn(len(ips))], nil - } else { + ips, err := r.lookupIP(ctx, host, D.TypeAAAA) + if err != nil { return netip.Addr{}, err + } else if len(ips) == 0 { + return netip.Addr{}, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, host) } + return ips[rand.Intn(len(ips))], nil } func (r *Resolver) shouldIPFallback(ip netip.Addr) bool { From 2a8e1778ad1d7b507e432f659407d76dffcaacb8 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 12 Nov 2022 21:42:45 +0800 Subject: [PATCH 106/250] chore: sync tunnel code --- tunnel/tunnel.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 349980c1..2e38da40 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -3,7 +3,6 @@ package tunnel import ( "context" "fmt" - P "github.com/Dreamacro/clash/component/process" "net" "net/netip" "path/filepath" @@ -14,6 +13,7 @@ import ( "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/component/nat" + P "github.com/Dreamacro/clash/component/process" "github.com/Dreamacro/clash/component/resolver" "github.com/Dreamacro/clash/component/sniffer" C "github.com/Dreamacro/clash/constant" @@ -365,7 +365,7 @@ func handleTCPConn(connCtx C.ConnContext) { err.Error(), ) } else { - log.Warnln("[TCP] dial %s (match %s(%s)) to %s error: %s", proxy.Name(), rule.RuleType().String(), rule.Payload(), metadata.RemoteAddress(), err.Error()) + log.Warnln("[TCP] dial %s (match %s/%s) %s --> %s error: %s", proxy.Name(), rule.RuleType().String(), rule.Payload(), metadata.SourceAddress(), metadata.RemoteAddress(), err.Error()) } return } From 6db7c800d5ffdd837eb930dd82623d1d1eac7412 Mon Sep 17 00:00:00 2001 From: Skimmle Date: Mon, 14 Nov 2022 20:17:12 +0800 Subject: [PATCH 107/250] fix: DoQ and HTTP/3 over proxy --- dns/doh.go | 169 +++++++++++++++++++++++++++-------------------------- dns/doq.go | 138 ++++++++++++++++++++++--------------------- 2 files changed, 158 insertions(+), 149 deletions(-) diff --git a/dns/doh.go b/dns/doh.go index 157ab67b..8c38e2f4 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -83,8 +83,9 @@ func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[strin } doh := &dnsOverHTTPS{ - url: u, - r: r, + url: u, + r: r, + proxyAdapter: proxyAdapter, quicConfig: &quic.Config{ KeepAlivePeriod: QUICKeepAlivePeriod, TokenStore: newQUICTokenStore(), @@ -98,8 +99,8 @@ func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[strin } // Address implements the Upstream interface for *dnsOverHTTPS. -func (p *dnsOverHTTPS) Address() string { return p.url.String() } -func (p *dnsOverHTTPS) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { +func (doh *dnsOverHTTPS) Address() string { return doh.url.String() } +func (doh *dnsOverHTTPS) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { // Quote from https://www.rfc-editor.org/rfc/rfc8484.html: // In order to maximize HTTP cache friendliness, DoH clients using media // formats that include the ID field from the DNS message header, such @@ -117,31 +118,31 @@ func (p *dnsOverHTTPS) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Ms // Check if there was already an active client before sending the request. // We'll only attempt to re-connect if there was one. - client, isCached, err := p.getClient() + client, isCached, err := doh.getClient() if err != nil { return nil, fmt.Errorf("failed to init http client: %w", err) } // Make the first attempt to send the DNS query. - msg, err = p.exchangeHTTPS(ctx, client, m) + msg, err = doh.exchangeHTTPS(ctx, client, m) // Make up to 2 attempts to re-create the HTTP client and send the request // again. There are several cases (mostly, with QUIC) where this workaround // is necessary to make HTTP client usable. We need to make 2 attempts in // the case when the connection was closed (due to inactivity for example) // AND the server refuses to open a 0-RTT connection. - for i := 0; isCached && p.shouldRetry(err) && i < 2; i++ { - client, err = p.resetClient(err) + for i := 0; isCached && doh.shouldRetry(err) && i < 2; i++ { + client, err = doh.resetClient(err) if err != nil { return nil, fmt.Errorf("failed to reset http client: %w", err) } - msg, err = p.exchangeHTTPS(ctx, client, m) + msg, err = doh.exchangeHTTPS(ctx, client, m) } if err != nil { // If the request failed anyway, make sure we don't use this client. - _, resErr := p.resetClient(err) + _, resErr := doh.resetClient(err) return nil, fmt.Errorf("err:%v,resErr:%v", err, resErr) } @@ -150,28 +151,28 @@ func (p *dnsOverHTTPS) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Ms } // Exchange implements the Upstream interface for *dnsOverHTTPS. -func (p *dnsOverHTTPS) Exchange(m *D.Msg) (*D.Msg, error) { - return p.ExchangeContext(context.Background(), m) +func (doh *dnsOverHTTPS) Exchange(m *D.Msg) (*D.Msg, error) { + return doh.ExchangeContext(context.Background(), m) } // Close implements the Upstream interface for *dnsOverHTTPS. -func (p *dnsOverHTTPS) Close() (err error) { - p.clientMu.Lock() - defer p.clientMu.Unlock() +func (doh *dnsOverHTTPS) Close() (err error) { + doh.clientMu.Lock() + defer doh.clientMu.Unlock() - runtime.SetFinalizer(p, nil) + runtime.SetFinalizer(doh, nil) - if p.client == nil { + if doh.client == nil { return nil } - return p.closeClient(p.client) + return doh.closeClient(doh.client) } // closeClient cleans up resources used by client if necessary. Note, that at // this point it should only be done for HTTP/3 as it may leak due to keep-alive // connections. -func (p *dnsOverHTTPS) closeClient(client *http.Client) (err error) { +func (doh *dnsOverHTTPS) closeClient(client *http.Client) (err error) { if isHTTP3(client) { return client.Transport.(io.Closer).Close() } @@ -180,15 +181,15 @@ func (p *dnsOverHTTPS) closeClient(client *http.Client) (err error) { } // exchangeHTTPS logs the request and its result and calls exchangeHTTPSClient. -func (p *dnsOverHTTPS) exchangeHTTPS(ctx context.Context, client *http.Client, req *D.Msg) (resp *D.Msg, err error) { - resp, err = p.exchangeHTTPSClient(ctx, client, req) +func (doh *dnsOverHTTPS) exchangeHTTPS(ctx context.Context, client *http.Client, req *D.Msg) (resp *D.Msg, err error) { + resp, err = doh.exchangeHTTPSClient(ctx, client, req) return resp, err } // exchangeHTTPSClient sends the DNS query to a DoH resolver using the specified // http.Client instance. -func (p *dnsOverHTTPS) exchangeHTTPSClient( +func (doh *dnsOverHTTPS) exchangeHTTPSClient( ctx context.Context, client *http.Client, req *D.Msg, @@ -206,10 +207,10 @@ func (p *dnsOverHTTPS) exchangeHTTPSClient( method = http3.MethodGet0RTT } - p.url.RawQuery = fmt.Sprintf("dns=%s", base64.RawURLEncoding.EncodeToString(buf)) - httpReq, err := http.NewRequest(method, p.url.String(), nil) + doh.url.RawQuery = fmt.Sprintf("dns=%s", base64.RawURLEncoding.EncodeToString(buf)) + httpReq, err := http.NewRequest(method, doh.url.String(), nil) if err != nil { - return nil, fmt.Errorf("creating http request to %s: %w", p.url, err) + return nil, fmt.Errorf("creating http request to %s: %w", doh.url, err) } httpReq.Header.Set("Accept", "application/dns-message") @@ -217,13 +218,13 @@ func (p *dnsOverHTTPS) exchangeHTTPSClient( _ = httpReq.WithContext(ctx) httpResp, err := client.Do(httpReq) if err != nil { - return nil, fmt.Errorf("requesting %s: %w", p.url, err) + return nil, fmt.Errorf("requesting %s: %w", doh.url, err) } defer httpResp.Body.Close() body, err := io.ReadAll(httpResp.Body) if err != nil { - return nil, fmt.Errorf("reading %s: %w", p.url, err) + return nil, fmt.Errorf("reading %s: %w", doh.url, err) } if httpResp.StatusCode != http.StatusOK { @@ -232,7 +233,7 @@ func (p *dnsOverHTTPS) exchangeHTTPSClient( "expected status %d, got %d from %s", http.StatusOK, httpResp.StatusCode, - p.url, + doh.url, ) } @@ -241,7 +242,7 @@ func (p *dnsOverHTTPS) exchangeHTTPSClient( if err != nil { return nil, fmt.Errorf( "unpacking response from %s: body is %s: %w", - p.url, + doh.url, body, err, ) @@ -256,7 +257,7 @@ func (p *dnsOverHTTPS) exchangeHTTPSClient( // shouldRetry checks what error we have received and returns true if we should // re-create the HTTP client and retry the request. -func (p *dnsOverHTTPS) shouldRetry(err error) (ok bool) { +func (doh *dnsOverHTTPS) shouldRetry(err error) (ok bool) { if err == nil { return false } @@ -281,57 +282,57 @@ func (p *dnsOverHTTPS) shouldRetry(err error) (ok bool) { // resetClient triggers re-creation of the *http.Client that is used by this // upstream. This method accepts the error that caused resetting client as // depending on the error we may also reset the QUIC config. -func (p *dnsOverHTTPS) resetClient(resetErr error) (client *http.Client, err error) { - p.clientMu.Lock() - defer p.clientMu.Unlock() +func (doh *dnsOverHTTPS) resetClient(resetErr error) (client *http.Client, err error) { + doh.clientMu.Lock() + defer doh.clientMu.Unlock() if errors.Is(resetErr, quic.Err0RTTRejected) { // Reset the TokenStore only if 0-RTT was rejected. - p.resetQUICConfig() + doh.resetQUICConfig() } - oldClient := p.client + oldClient := doh.client if oldClient != nil { - closeErr := p.closeClient(oldClient) + closeErr := doh.closeClient(oldClient) if closeErr != nil { log.Warnln("warning: failed to close the old http client: %v", closeErr) } } log.Debugln("re-creating the http client due to %v", resetErr) - p.client, err = p.createClient() + doh.client, err = doh.createClient() - return p.client, err + return doh.client, err } // getQUICConfig returns the QUIC config in a thread-safe manner. Note, that // this method returns a pointer, it is forbidden to change its properties. -func (p *dnsOverHTTPS) getQUICConfig() (c *quic.Config) { - p.quicConfigGuard.Lock() - defer p.quicConfigGuard.Unlock() +func (doh *dnsOverHTTPS) getQUICConfig() (c *quic.Config) { + doh.quicConfigGuard.Lock() + defer doh.quicConfigGuard.Unlock() - return p.quicConfig + return doh.quicConfig } // resetQUICConfig Re-create the token store to make sure we're not trying to // use invalid for 0-RTT. -func (p *dnsOverHTTPS) resetQUICConfig() { - p.quicConfigGuard.Lock() - defer p.quicConfigGuard.Unlock() +func (doh *dnsOverHTTPS) resetQUICConfig() { + doh.quicConfigGuard.Lock() + defer doh.quicConfigGuard.Unlock() - p.quicConfig = p.quicConfig.Clone() - p.quicConfig.TokenStore = newQUICTokenStore() + doh.quicConfig = doh.quicConfig.Clone() + doh.quicConfig.TokenStore = newQUICTokenStore() } // getClient gets or lazily initializes an HTTP client (and transport) that will // be used for this DoH resolver. -func (p *dnsOverHTTPS) getClient() (c *http.Client, isCached bool, err error) { +func (doh *dnsOverHTTPS) getClient() (c *http.Client, isCached bool, err error) { startTime := time.Now() - p.clientMu.Lock() - defer p.clientMu.Unlock() - if p.client != nil { - return p.client, true, nil + doh.clientMu.Lock() + defer doh.clientMu.Unlock() + if doh.client != nil { + return doh.client, true, nil } // Timeout can be exceeded while waiting for the lock. This happens quite @@ -342,17 +343,17 @@ func (p *dnsOverHTTPS) getClient() (c *http.Client, isCached bool, err error) { } log.Debugln("creating a new http client") - p.client, err = p.createClient() + doh.client, err = doh.createClient() - return p.client, false, err + return doh.client, false, err } // createClient creates a new *http.Client instance. The HTTP protocol version // will depend on whether HTTP3 is allowed and provided by this upstream. Note, // that we'll attempt to establish a QUIC connection when creating the client in // order to check whether HTTP3 is supported. -func (p *dnsOverHTTPS) createClient() (*http.Client, error) { - transport, err := p.createTransport() +func (doh *dnsOverHTTPS) createClient() (*http.Client, error) { + transport, err := doh.createTransport() if err != nil { return nil, fmt.Errorf("initializing http transport: %w", err) } @@ -363,9 +364,9 @@ func (p *dnsOverHTTPS) createClient() (*http.Client, error) { Jar: nil, } - p.client = client + doh.client = client - return p.client, nil + return doh.client, nil } // createTransport initializes an HTTP transport that will be used specifically @@ -374,7 +375,7 @@ func (p *dnsOverHTTPS) createClient() (*http.Client, error) { // that this function will first attempt to establish a QUIC connection (if // HTTP3 is enabled in the upstream options). If this attempt is successful, // it returns an HTTP3 transport, otherwise it returns the H1/H2 transport. -func (p *dnsOverHTTPS) createTransport() (t http.RoundTripper, err error) { +func (doh *dnsOverHTTPS) createTransport() (t http.RoundTripper, err error) { tlsConfig := tlsC.GetGlobalFingerprintTLCConfig( &tls.Config{ InsecureSkipVerify: false, @@ -382,15 +383,15 @@ func (p *dnsOverHTTPS) createTransport() (t http.RoundTripper, err error) { SessionTicketsDisabled: false, }) var nextProtos []string - for _, v := range p.httpVersions { + for _, v := range doh.httpVersions { nextProtos = append(nextProtos, string(v)) } tlsConfig.NextProtos = nextProtos - dialContext := getDialHandler(p.r, p.proxyAdapter) + dialContext := getDialHandler(doh.r, doh.proxyAdapter) // First, we attempt to create an HTTP3 transport. If the probe QUIC // connection is established successfully, we'll be using HTTP3 for this // upstream. - transportH3, err := p.createTransportH3(tlsConfig, dialContext) + transportH3, err := doh.createTransportH3(tlsConfig, dialContext) if err == nil { log.Debugln("using HTTP/3 for this upstream: QUIC was faster") return transportH3, nil @@ -398,7 +399,7 @@ func (p *dnsOverHTTPS) createTransport() (t http.RoundTripper, err error) { log.Debugln("using HTTP/2 for this upstream: %v", err) - if !p.supportsHTTP() { + if !doh.supportsHTTP() { return nil, errors.New("HTTP1/1 and HTTP2 are not supported by this upstream") } @@ -551,14 +552,14 @@ func (doh *dnsOverHTTPS) dialQuic(ctx context.Context, addr string, tlsCfg *tls. // probeH3 runs a test to check whether QUIC is faster than TLS for this // upstream. If the test is successful it will return the address that we // should use to establish the QUIC connections. -func (p *dnsOverHTTPS) probeH3( +func (doh *dnsOverHTTPS) probeH3( tlsConfig *tls.Config, dialContext dialHandler, ) (addr string, err error) { // We're using bootstrapped address instead of what's passed to the function // it does not create an actual connection, but it helps us determine // what IP is actually reachable (when there are v4/v6 addresses). - rawConn, err := dialContext(context.Background(), "udp", p.url.Host) + rawConn, err := dialContext(context.Background(), "udp", doh.url.Host) if err != nil { return "", fmt.Errorf("failed to dial: %w", err) } @@ -567,13 +568,17 @@ func (p *dnsOverHTTPS) probeH3( udpConn, ok := rawConn.(*net.UDPConn) if !ok { - return "", fmt.Errorf("not a UDP connection to %s", p.Address()) + if packetConn, ok := rawConn.(*wrapPacketConn); !ok { + return "", fmt.Errorf("not a UDP connection to %s", doh.Address()) + } else { + addr = packetConn.RemoteAddr().String() + } + } else { + addr = udpConn.RemoteAddr().String() } - addr = udpConn.RemoteAddr().String() - // Avoid spending time on probing if this upstream only supports HTTP/3. - if p.supportsH3() && !p.supportsHTTP() { + if doh.supportsH3() && !doh.supportsHTTP() { return addr, nil } @@ -593,8 +598,8 @@ func (p *dnsOverHTTPS) probeH3( // Run probeQUIC and probeTLS in parallel and see which one is faster. chQuic := make(chan error, 1) chTLS := make(chan error, 1) - go p.probeQUIC(addr, probeTLSCfg, chQuic) - go p.probeTLS(dialContext, probeTLSCfg, chTLS) + go doh.probeQUIC(addr, probeTLSCfg, chQuic) + go doh.probeTLS(dialContext, probeTLSCfg, chTLS) select { case quicErr := <-chQuic: @@ -618,16 +623,16 @@ func (p *dnsOverHTTPS) probeH3( // probeQUIC attempts to establish a QUIC connection to the specified address. // We run probeQUIC and probeTLS in parallel and see which one is faster. -func (p *dnsOverHTTPS) probeQUIC(addr string, tlsConfig *tls.Config, ch chan error) { +func (doh *dnsOverHTTPS) probeQUIC(addr string, tlsConfig *tls.Config, ch chan error) { startTime := time.Now() timeout := DefaultTimeout ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(timeout)) defer cancel() - conn, err := p.dialQuic(ctx, addr, tlsConfig, p.getQUICConfig()) + conn, err := doh.dialQuic(ctx, addr, tlsConfig, doh.getQUICConfig()) if err != nil { - ch <- fmt.Errorf("opening QUIC connection to %s: %w", p.Address(), err) + ch <- fmt.Errorf("opening QUIC connection to %s: %w", doh.Address(), err) return } @@ -642,10 +647,10 @@ func (p *dnsOverHTTPS) probeQUIC(addr string, tlsConfig *tls.Config, ch chan err // probeTLS attempts to establish a TLS connection to the specified address. We // run probeQUIC and probeTLS in parallel and see which one is faster. -func (p *dnsOverHTTPS) probeTLS(dialContext dialHandler, tlsConfig *tls.Config, ch chan error) { +func (doh *dnsOverHTTPS) probeTLS(dialContext dialHandler, tlsConfig *tls.Config, ch chan error) { startTime := time.Now() - conn, err := p.tlsDial(dialContext, "tcp", tlsConfig) + conn, err := doh.tlsDial(dialContext, "tcp", tlsConfig) if err != nil { ch <- fmt.Errorf("opening TLS connection: %w", err) return @@ -661,8 +666,8 @@ func (p *dnsOverHTTPS) probeTLS(dialContext dialHandler, tlsConfig *tls.Config, } // supportsH3 returns true if HTTP/3 is supported by this upstream. -func (p *dnsOverHTTPS) supportsH3() (ok bool) { - for _, v := range p.supportedHTTPVersions() { +func (doh *dnsOverHTTPS) supportsH3() (ok bool) { + for _, v := range doh.supportedHTTPVersions() { if v == C.HTTPVersion3 { return true } @@ -672,8 +677,8 @@ func (p *dnsOverHTTPS) supportsH3() (ok bool) { } // supportsHTTP returns true if HTTP/1.1 or HTTP2 is supported by this upstream. -func (p *dnsOverHTTPS) supportsHTTP() (ok bool) { - for _, v := range p.supportedHTTPVersions() { +func (doh *dnsOverHTTPS) supportsHTTP() (ok bool) { + for _, v := range doh.supportedHTTPVersions() { if v == C.HTTPVersion11 || v == C.HTTPVersion2 { return true } @@ -683,8 +688,8 @@ func (p *dnsOverHTTPS) supportsHTTP() (ok bool) { } // supportedHTTPVersions returns the list of supported HTTP versions. -func (p *dnsOverHTTPS) supportedHTTPVersions() (v []C.HTTPVersion) { - v = p.httpVersions +func (doh *dnsOverHTTPS) supportedHTTPVersions() (v []C.HTTPVersion) { + v = doh.httpVersions if v == nil { v = DefaultHTTPVersions } diff --git a/dns/doq.go b/dns/doq.go index 07cceff6..316417ef 100644 --- a/dns/doq.go +++ b/dns/doq.go @@ -88,9 +88,9 @@ func newDoQ(resolver *Resolver, addr string, adapter string) (dnsClient, error) } // Address implements the Upstream interface for *dnsOverQUIC. -func (p *dnsOverQUIC) Address() string { return p.addr } +func (doq *dnsOverQUIC) Address() string { return doq.addr } -func (p *dnsOverQUIC) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { +func (doq *dnsOverQUIC) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { // When sending queries over a QUIC connection, the DNS Message ID MUST be // set to zero. id := m.Id @@ -105,49 +105,49 @@ func (p *dnsOverQUIC) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg // Check if there was already an active conn before sending the request. // We'll only attempt to re-connect if there was one. - hasConnection := p.hasConnection() + hasConnection := doq.hasConnection() // Make the first attempt to send the DNS query. - msg, err = p.exchangeQUIC(ctx, m) + msg, err = doq.exchangeQUIC(ctx, m) // Make up to 2 attempts to re-open the QUIC connection and send the request // again. There are several cases where this workaround is necessary to // make DoQ usable. We need to make 2 attempts in the case when the // connection was closed (due to inactivity for example) AND the server // refuses to open a 0-RTT connection. - for i := 0; hasConnection && p.shouldRetry(err) && i < 2; i++ { + for i := 0; hasConnection && doq.shouldRetry(err) && i < 2; i++ { log.Debugln("re-creating the QUIC connection and retrying due to %v", err) // Close the active connection to make sure we'll try to re-connect. - p.closeConnWithError(err) + doq.closeConnWithError(err) // Retry sending the request. - msg, err = p.exchangeQUIC(ctx, m) + msg, err = doq.exchangeQUIC(ctx, m) } if err != nil { // If we're unable to exchange messages, make sure the connection is // closed and signal about an internal error. - p.closeConnWithError(err) + doq.closeConnWithError(err) } return msg, err } // Exchange implements the Upstream interface for *dnsOverQUIC. -func (p *dnsOverQUIC) Exchange(m *D.Msg) (msg *D.Msg, err error) { - return p.ExchangeContext(context.Background(), m) +func (doq *dnsOverQUIC) Exchange(m *D.Msg) (msg *D.Msg, err error) { + return doq.ExchangeContext(context.Background(), m) } // Close implements the Upstream interface for *dnsOverQUIC. -func (p *dnsOverQUIC) Close() (err error) { - p.connMu.Lock() - defer p.connMu.Unlock() +func (doq *dnsOverQUIC) Close() (err error) { + doq.connMu.Lock() + defer doq.connMu.Unlock() - runtime.SetFinalizer(p, nil) + runtime.SetFinalizer(doq, nil) - if p.conn != nil { - err = p.conn.CloseWithError(QUICCodeNoError, "") + if doq.conn != nil { + err = doq.conn.CloseWithError(QUICCodeNoError, "") } return err @@ -155,9 +155,9 @@ func (p *dnsOverQUIC) Close() (err error) { // exchangeQUIC attempts to open a QUIC connection, send the DNS message // through it and return the response it got from the server. -func (p *dnsOverQUIC) exchangeQUIC(ctx context.Context, msg *D.Msg) (resp *D.Msg, err error) { +func (doq *dnsOverQUIC) exchangeQUIC(ctx context.Context, msg *D.Msg) (resp *D.Msg, err error) { var conn quic.Connection - conn, err = p.getConnection(true) + conn, err = doq.getConnection(true) if err != nil { return nil, err } @@ -169,7 +169,7 @@ func (p *dnsOverQUIC) exchangeQUIC(ctx context.Context, msg *D.Msg) (resp *D.Msg } var stream quic.Stream - stream, err = p.openStream(ctx, conn) + stream, err = doq.openStream(ctx, conn) if err != nil { return nil, err } @@ -185,7 +185,7 @@ func (p *dnsOverQUIC) exchangeQUIC(ctx context.Context, msg *D.Msg) (resp *D.Msg // write-direction of the stream, but does not prevent reading from it. _ = stream.Close() - return p.readMsg(stream) + return doq.readMsg(stream) } // AddPrefix adds a 2-byte prefix with the DNS message length. @@ -199,17 +199,17 @@ func AddPrefix(b []byte) (m []byte) { // shouldRetry checks what error we received and decides whether it is required // to re-open the connection and retry sending the request. -func (p *dnsOverQUIC) shouldRetry(err error) (ok bool) { +func (doq *dnsOverQUIC) shouldRetry(err error) (ok bool) { return isQUICRetryError(err) } // getBytesPool returns (creates if needed) a pool we store byte buffers in. -func (p *dnsOverQUIC) getBytesPool() (pool *sync.Pool) { - p.bytesPoolGuard.Lock() - defer p.bytesPoolGuard.Unlock() +func (doq *dnsOverQUIC) getBytesPool() (pool *sync.Pool) { + doq.bytesPoolGuard.Lock() + defer doq.bytesPoolGuard.Unlock() - if p.bytesPool == nil { - p.bytesPool = &sync.Pool{ + if doq.bytesPool == nil { + doq.bytesPool = &sync.Pool{ New: func() interface{} { b := make([]byte, MaxMsgSize) @@ -218,19 +218,19 @@ func (p *dnsOverQUIC) getBytesPool() (pool *sync.Pool) { } } - return p.bytesPool + return doq.bytesPool } // getConnection opens or returns an existing quic.Connection. useCached // argument controls whether we should try to use the existing cached // connection. If it is false, we will forcibly create a new connection and // close the existing one if needed. -func (p *dnsOverQUIC) getConnection(useCached bool) (quic.Connection, error) { +func (doq *dnsOverQUIC) getConnection(useCached bool) (quic.Connection, error) { var conn quic.Connection - p.connMu.RLock() - conn = p.conn + doq.connMu.RLock() + conn = doq.conn if conn != nil && useCached { - p.connMu.RUnlock() + doq.connMu.RUnlock() return conn, nil } @@ -238,50 +238,50 @@ func (p *dnsOverQUIC) getConnection(useCached bool) (quic.Connection, error) { // we're recreating the connection, let's create a new one. _ = conn.CloseWithError(QUICCodeNoError, "") } - p.connMu.RUnlock() + doq.connMu.RUnlock() - p.connMu.Lock() - defer p.connMu.Unlock() + doq.connMu.Lock() + defer doq.connMu.Unlock() var err error - conn, err = p.openConnection() + conn, err = doq.openConnection() if err != nil { return nil, err } - p.conn = conn + doq.conn = conn return conn, nil } // hasConnection returns true if there's an active QUIC connection. -func (p *dnsOverQUIC) hasConnection() (ok bool) { - p.connMu.Lock() - defer p.connMu.Unlock() +func (doq *dnsOverQUIC) hasConnection() (ok bool) { + doq.connMu.Lock() + defer doq.connMu.Unlock() - return p.conn != nil + return doq.conn != nil } // getQUICConfig returns the QUIC config in a thread-safe manner. Note, that // this method returns a pointer, it is forbidden to change its properties. -func (p *dnsOverQUIC) getQUICConfig() (c *quic.Config) { - p.quicConfigGuard.Lock() - defer p.quicConfigGuard.Unlock() +func (doq *dnsOverQUIC) getQUICConfig() (c *quic.Config) { + doq.quicConfigGuard.Lock() + defer doq.quicConfigGuard.Unlock() - return p.quicConfig + return doq.quicConfig } // resetQUICConfig re-creates the tokens store as we may need to use a new one // if we failed to connect. -func (p *dnsOverQUIC) resetQUICConfig() { - p.quicConfigGuard.Lock() - defer p.quicConfigGuard.Unlock() +func (doq *dnsOverQUIC) resetQUICConfig() { + doq.quicConfigGuard.Lock() + defer doq.quicConfigGuard.Unlock() - p.quicConfig = p.quicConfig.Clone() - p.quicConfig.TokenStore = newQUICTokenStore() + doq.quicConfig = doq.quicConfig.Clone() + doq.quicConfig.TokenStore = newQUICTokenStore() } // openStream opens a new QUIC stream for the specified connection. -func (p *dnsOverQUIC) openStream(ctx context.Context, conn quic.Connection) (quic.Stream, error) { +func (doq *dnsOverQUIC) openStream(ctx context.Context, conn quic.Connection) (quic.Stream, error) { ctx, cancel := context.WithCancel(ctx) defer cancel() @@ -292,7 +292,7 @@ func (p *dnsOverQUIC) openStream(ctx context.Context, conn quic.Connection) (qui // We can get here if the old QUIC connection is not valid anymore. We // should try to re-create the connection again in this case. - newConn, err := p.getConnection(false) + newConn, err := doq.getConnection(false) if err != nil { return nil, err } @@ -321,14 +321,18 @@ func (doq *dnsOverQUIC) openConnection() (conn quic.Connection, err error) { // It's never actually used _ = rawConn.Close() cancel() - + var addr string udpConn, ok := rawConn.(*net.UDPConn) if !ok { - return nil, fmt.Errorf("failed to open connection to %s", doq.addr) + if packetConn, ok := rawConn.(*wrapPacketConn); !ok { + return nil, fmt.Errorf("failed to open connection to %s", doq.addr) + } else { + addr = packetConn.RemoteAddr().String() + } + } else { + addr = udpConn.RemoteAddr().String() } - addr := udpConn.RemoteAddr().String() - ip, port, err := net.SplitHostPort(addr) if err != nil { return nil, err @@ -379,11 +383,11 @@ func (doq *dnsOverQUIC) openConnection() (conn quic.Connection, err error) { // closeConnWithError closes the active connection with error to make sure that // new queries were processed in another connection. We can do that in the case // of a fatal error. -func (p *dnsOverQUIC) closeConnWithError(err error) { - p.connMu.Lock() - defer p.connMu.Unlock() +func (doq *dnsOverQUIC) closeConnWithError(err error) { + doq.connMu.Lock() + defer doq.connMu.Unlock() - if p.conn == nil { + if doq.conn == nil { // Do nothing, there's no active conn anyways. return } @@ -395,19 +399,19 @@ func (p *dnsOverQUIC) closeConnWithError(err error) { if errors.Is(err, quic.Err0RTTRejected) { // Reset the TokenStore only if 0-RTT was rejected. - p.resetQUICConfig() + doq.resetQUICConfig() } - err = p.conn.CloseWithError(code, "") + err = doq.conn.CloseWithError(code, "") if err != nil { log.Errorln("failed to close the conn: %v", err) } - p.conn = nil + doq.conn = nil } // readMsg reads the incoming DNS message from the QUIC stream. -func (p *dnsOverQUIC) readMsg(stream quic.Stream) (m *D.Msg, err error) { - pool := p.getBytesPool() +func (doq *dnsOverQUIC) readMsg(stream quic.Stream) (m *D.Msg, err error) { + pool := doq.getBytesPool() bufPtr := pool.Get().(*[]byte) defer pool.Put(bufPtr) @@ -415,7 +419,7 @@ func (p *dnsOverQUIC) readMsg(stream quic.Stream) (m *D.Msg, err error) { respBuf := *bufPtr n, err := stream.Read(respBuf) if err != nil && n == 0 { - return nil, fmt.Errorf("reading response from %s: %w", p.Address(), err) + return nil, fmt.Errorf("reading response from %s: %w", doq.Address(), err) } // All DNS messages (queries and responses) sent over DoQ connections MUST @@ -426,7 +430,7 @@ func (p *dnsOverQUIC) readMsg(stream quic.Stream) (m *D.Msg, err error) { m = new(D.Msg) err = m.Unpack(respBuf[2:]) if err != nil { - return nil, fmt.Errorf("unpacking response from %s: %w", p.Address(), err) + return nil, fmt.Errorf("unpacking response from %s: %w", doq.Address(), err) } return m, nil @@ -512,7 +516,7 @@ func getDialHandler(r *Resolver, proxyAdapter string) dialHandler { if len(proxyAdapter) == 0 { return dialer.DialContext(ctx, network, net.JoinHostPort(ip.String(), port), dialer.WithDirect()) } else { - return dialContextExtra(ctx, proxyAdapter, network, ip.Unmap(), port, dialer.WithDirect()) + return dialContextExtra(ctx, proxyAdapter, network, ip.Unmap(), port) } } } From 9c8e39827f5f70ab6f197e951b6e99b3aea74021 Mon Sep 17 00:00:00 2001 From: zhudan Date: Mon, 14 Nov 2022 12:11:54 +0800 Subject: [PATCH 108/250] update tfo to v2 --- go.mod | 2 +- go.sum | 2 ++ listener/http/server.go | 2 +- listener/mixed/mixed.go | 2 +- listener/socks/tcp.go | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 57cc35de..36aa0084 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.19 require ( github.com/cilium/ebpf v0.9.3 github.com/coreos/go-iptables v0.6.0 - github.com/database64128/tfo-go v1.1.2 github.com/dlclark/regexp2 v1.7.0 github.com/go-chi/chi/v5 v5.0.7 github.com/go-chi/cors v1.2.1 @@ -49,6 +48,7 @@ replace github.com/sagernet/sing-tun => github.com/MetaCubeX/sing-tun v0.0.0-202 require ( github.com/ajg/form v1.5.1 // indirect + github.com/database64128/tfo-go/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect diff --git a/go.sum b/go.sum index 06551cdc..82a2def7 100644 --- a/go.sum +++ b/go.sum @@ -13,6 +13,8 @@ github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= github.com/database64128/tfo-go v1.1.2 h1:GwxtJp09BdUTVEoeT421t231eNZoGOCRkklbl4WI1kU= github.com/database64128/tfo-go v1.1.2/go.mod h1:jgrSUPyOvTGQyn6irCOpk7L2W/q/0VLZZcovQiMi+bI= +github.com/database64128/tfo-go/v2 v2.0.2 h1:5rGgkJeLEKlNaqredfrPQNLnctn1b+1fq/8tdKdOzJg= +github.com/database64128/tfo-go/v2 v2.0.2/go.mod h1:FDdt4JaAsRU66wsYHxSVytYimPkKIHupVsxM+5DhvjY= 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= diff --git a/listener/http/server.go b/listener/http/server.go index bd3a198d..f507fac7 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -2,7 +2,7 @@ package http import ( "context" - "github.com/database64128/tfo-go" + "github.com/database64128/tfo-go/v2" "net" "github.com/Dreamacro/clash/common/cache" diff --git a/listener/mixed/mixed.go b/listener/mixed/mixed.go index acdcff33..0fcaa80f 100644 --- a/listener/mixed/mixed.go +++ b/listener/mixed/mixed.go @@ -2,7 +2,7 @@ package mixed import ( "context" - "github.com/database64128/tfo-go" + "github.com/database64128/tfo-go/v2" "net" "github.com/Dreamacro/clash/common/cache" diff --git a/listener/socks/tcp.go b/listener/socks/tcp.go index 8b505e74..13a33d1b 100644 --- a/listener/socks/tcp.go +++ b/listener/socks/tcp.go @@ -2,7 +2,7 @@ package socks import ( "context" - "github.com/database64128/tfo-go" + "github.com/database64128/tfo-go/v2" "io" "net" From 0eecd11fdc82e3bc7ee335c04d72f036c920914e Mon Sep 17 00:00:00 2001 From: zhudan Date: Mon, 14 Nov 2022 12:24:37 +0800 Subject: [PATCH 109/250] ss and vmess inbound add tfo --- listener/listener.go | 4 ++-- listener/shadowsocks/tcp.go | 9 +++++++-- listener/sing_shadowsocks/server.go | 10 +++++++--- listener/sing_vmess/server.go | 8 ++++++-- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/listener/listener.go b/listener/listener.go index 880f56d4..ff7eb9d1 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -280,7 +280,7 @@ func ReCreateShadowSocks(shadowSocksConfig string, tcpIn chan<- C.ConnContext, u return } - listener, err := sing_shadowsocks.New(shadowSocksConfig, tcpIn, udpIn) + listener, err := sing_shadowsocks.New(shadowSocksConfig, inboundTfo, tcpIn, udpIn) if err != nil { return } @@ -320,7 +320,7 @@ func ReCreateVmess(vmessConfig string, tcpIn chan<- C.ConnContext, udpIn chan<- return } - listener, err := sing_vmess.New(vmessConfig, tcpIn, udpIn) + listener, err := sing_vmess.New(vmessConfig, inboundTfo, tcpIn, udpIn) if err != nil { return } diff --git a/listener/shadowsocks/tcp.go b/listener/shadowsocks/tcp.go index b540a632..37d29e61 100644 --- a/listener/shadowsocks/tcp.go +++ b/listener/shadowsocks/tcp.go @@ -1,6 +1,8 @@ package shadowsocks import ( + "context" + "github.com/database64128/tfo-go/v2" "net" "strings" @@ -21,7 +23,7 @@ type Listener struct { var _listener *Listener -func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (*Listener, error) { +func New(config string, inboundTfo bool, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (*Listener, error) { addr, cipher, password, err := ParseSSURL(config) if err != nil { return nil, err @@ -46,7 +48,10 @@ func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.Packet sl.udpListeners = append(sl.udpListeners, ul) //TCP - l, err := net.Listen("tcp", addr) + lc := tfo.ListenConfig{ + DisableTFO: !inboundTfo, + } + l, err := lc.Listen(context.Background(), "tcp", addr) if err != nil { return nil, err } diff --git a/listener/sing_shadowsocks/server.go b/listener/sing_shadowsocks/server.go index ba61ea36..bd500625 100644 --- a/listener/sing_shadowsocks/server.go +++ b/listener/sing_shadowsocks/server.go @@ -3,6 +3,7 @@ package sing_shadowsocks import ( "context" "fmt" + "github.com/database64128/tfo-go/v2" "net" "strings" @@ -32,7 +33,7 @@ type Listener struct { var _listener *Listener -func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (C.AdvanceListener, error) { +func New(config string, inboundTfo bool, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (C.AdvanceListener, error) { addr, cipher, password, err := embedSS.ParseSSURL(config) if err != nil { return nil, err @@ -56,7 +57,7 @@ func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.Packet sl.service, err = shadowaead_2022.NewServiceWithPassword(cipher, password, udpTimeout, h) default: err = fmt.Errorf("shadowsocks: unsupported method: %s", cipher) - return embedSS.New(config, tcpIn, udpIn) + return embedSS.New(config, inboundTfo, tcpIn, udpIn) } if err != nil { return nil, err @@ -100,7 +101,10 @@ func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.Packet }() //TCP - l, err := net.Listen("tcp", addr) + lc := tfo.ListenConfig{ + DisableTFO: !inboundTfo, + } + l, err := lc.Listen(context.Background(), "tcp", addr) if err != nil { return nil, err } diff --git a/listener/sing_vmess/server.go b/listener/sing_vmess/server.go index 26b12893..f859d848 100644 --- a/listener/sing_vmess/server.go +++ b/listener/sing_vmess/server.go @@ -2,6 +2,7 @@ package sing_vmess import ( "context" + "github.com/database64128/tfo-go/v2" "net" "net/url" "strings" @@ -24,7 +25,7 @@ type Listener struct { var _listener *Listener -func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (*Listener, error) { +func New(config string, inboundTfo bool, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (*Listener, error) { addr, username, password, err := parseVmessURL(config) if err != nil { return nil, err @@ -54,7 +55,10 @@ func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.Packet addr := addr //TCP - l, err := net.Listen("tcp", addr) + lc := tfo.ListenConfig{ + DisableTFO: !inboundTfo, + } + l, err := lc.Listen(context.Background(), "tcp", addr) if err != nil { return nil, err } From e5a81b6c3544fe8dda969d5a9918ffd75d40c635 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 15 Nov 2022 15:07:04 +0800 Subject: [PATCH 110/250] fix: don't use ReadOnceFrom in sing-shadowsocks --- go.mod | 10 ++++++---- go.sum | 16 ++++++++-------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 36aa0084..399e89f1 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/oschwald/geoip2-golang v1.8.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4 - github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 + github.com/sagernet/sing-shadowsocks v0.0.0-20221112030934-e55284e180ea github.com/sagernet/sing-tun v0.0.0-20221012082254-488c3b75f6fd github.com/sagernet/sing-vmess v0.0.0-20221109021549-b446d5bdddf0 github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c @@ -32,11 +32,11 @@ require ( go.etcd.io/bbolt v1.3.6 go.uber.org/atomic v1.10.0 go.uber.org/automaxprocs v1.5.1 - golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077 + golang.org/x/crypto v0.2.0 golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 - golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0 + golang.org/x/net v0.2.0 golang.org/x/sync v0.1.0 - golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06 + golang.org/x/sys v0.2.0 google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v3 v3.0.1 @@ -46,6 +46,8 @@ replace github.com/lucas-clemente/quic-go => github.com/HyNetwork/quic-go v0.30. replace github.com/sagernet/sing-tun => github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc +replace github.com/sagernet/sing-shadowsocks => github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221115070342-5f9900cebd8b + require ( github.com/ajg/form v1.5.1 // indirect github.com/database64128/tfo-go/v2 v2.0.2 // indirect diff --git a/go.sum b/go.sum index 82a2def7..d88c641b 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/HyNetwork/quic-go v0.30.1-0.20221105180419-83715d7269a8 h1:FBo40lMrk1bZZzJRJx8U+bQUPhLDGTUJ/Q5NV5BbO4Q= github.com/HyNetwork/quic-go v0.30.1-0.20221105180419-83715d7269a8/go.mod h1:ssOrRsOmdxa768Wr78vnh2B8JozgLsMzG/g+0qEC7uk= +github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221115070342-5f9900cebd8b h1:DZ5F0w2b1pAh/IpnB8SP2P22pq6NfafyzyPtrhzzG2g= +github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221115070342-5f9900cebd8b/go.mod h1:16sNARQbsFbYIzAuPySszQA6Wfgzk7GWSzh1a6kDrUU= github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc h1:B1vmR4cwDjJQ6YM8NkuXmt9vIYOdV/+qA+F3UAFE3YY= github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc/go.mod h1:1u3pjXA9HmH7kRiBJqM3C/zPxrxnCLd3svmqtub/RFU= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= @@ -111,8 +113,6 @@ github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJ github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4 h1:LO7xMvMGhYmjQg2vjhTzsODyzs9/WLYu5Per+/8jIeo= github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4= -github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4= -github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM= github.com/sagernet/sing-vmess v0.0.0-20221109021549-b446d5bdddf0 h1:z3kuD3hPNdEq7/wVy5lwE21f+8ZTazBtR81qswxJoCc= github.com/sagernet/sing-vmess v0.0.0-20221109021549-b446d5bdddf0/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM= github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c h1:qP3ZOHnjZalvqbjundbXiv/YrNlo3HOgrKc+S1QGs0U= @@ -147,8 +147,8 @@ go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66 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.1.1-0.20221024173537-a3485e174077 h1:t5bjOfJPQfaG9NV1imLZM5E2uzaLGs5/NtyMtRNVjQ4= -golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.2.0 h1:BRXPfhNivWL5Yq0BGQ39a2sW6t44aODpfxkWjYdzewE= +golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 h1:RjggHMcaTVp0LOVZcW0bo8alwHrOaCrGUDgfWUHhnN4= golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -168,8 +168,8 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0 h1:vZ44Ys50wUISbPd+jC8cRLSvhyfX9Ii/ZmDnn/aiJtM= -golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= @@ -198,8 +198,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06 h1:E1pm64FqQa4v8dHd/bAneyMkR4hk8LTJhoSlc5mc1cM= -golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= From eb8431255d5bd3d728d78ec8265191d65cd66b4b Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 15 Nov 2022 20:20:57 +0800 Subject: [PATCH 111/250] fix: sing-shadowsocks serverConn.Write return (0,nil) when p isn't empty --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 399e89f1..fb9e6f2b 100644 --- a/go.mod +++ b/go.mod @@ -46,7 +46,7 @@ replace github.com/lucas-clemente/quic-go => github.com/HyNetwork/quic-go v0.30. replace github.com/sagernet/sing-tun => github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc -replace github.com/sagernet/sing-shadowsocks => github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221115070342-5f9900cebd8b +replace github.com/sagernet/sing-shadowsocks => github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221115121930-f1357ec9c15e require ( github.com/ajg/form v1.5.1 // indirect diff --git a/go.sum b/go.sum index d88c641b..60692a07 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/HyNetwork/quic-go v0.30.1-0.20221105180419-83715d7269a8 h1:FBo40lMrk1bZZzJRJx8U+bQUPhLDGTUJ/Q5NV5BbO4Q= github.com/HyNetwork/quic-go v0.30.1-0.20221105180419-83715d7269a8/go.mod h1:ssOrRsOmdxa768Wr78vnh2B8JozgLsMzG/g+0qEC7uk= -github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221115070342-5f9900cebd8b h1:DZ5F0w2b1pAh/IpnB8SP2P22pq6NfafyzyPtrhzzG2g= -github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221115070342-5f9900cebd8b/go.mod h1:16sNARQbsFbYIzAuPySszQA6Wfgzk7GWSzh1a6kDrUU= +github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221115121930-f1357ec9c15e h1:61mKDq9ldYCH3Yb/Pelib+ISIXegprbm1CSPGB+HpT4= +github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221115121930-f1357ec9c15e/go.mod h1:16sNARQbsFbYIzAuPySszQA6Wfgzk7GWSzh1a6kDrUU= github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc h1:B1vmR4cwDjJQ6YM8NkuXmt9vIYOdV/+qA+F3UAFE3YY= github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc/go.mod h1:1u3pjXA9HmH7kRiBJqM3C/zPxrxnCLd3svmqtub/RFU= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= From 03645fb2356ee3db6db236629ce43620a85e7cca Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 16 Nov 2022 10:00:09 +0800 Subject: [PATCH 112/250] fix: correct the go.mod --- go.mod | 2 +- go.sum | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/go.mod b/go.mod index fb9e6f2b..8c70783e 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.19 require ( github.com/cilium/ebpf v0.9.3 github.com/coreos/go-iptables v0.6.0 + github.com/database64128/tfo-go/v2 v2.0.2 github.com/dlclark/regexp2 v1.7.0 github.com/go-chi/chi/v5 v5.0.7 github.com/go-chi/cors v1.2.1 @@ -50,7 +51,6 @@ replace github.com/sagernet/sing-shadowsocks => github.com/MetaCubeX/sing-shadow require ( github.com/ajg/form v1.5.1 // indirect - github.com/database64128/tfo-go/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect diff --git a/go.sum b/go.sum index 60692a07..96c25304 100644 --- a/go.sum +++ b/go.sum @@ -13,8 +13,6 @@ github.com/cilium/ebpf v0.9.3 h1:5KtxXZU+scyERvkJMEm16TbScVvuuMrlhPly78ZMbSc= github.com/cilium/ebpf v0.9.3/go.mod h1:w27N4UjpaQ9X/DGrSugxUG+H+NhgntDuPb5lCzxCn8A= github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk= github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= -github.com/database64128/tfo-go v1.1.2 h1:GwxtJp09BdUTVEoeT421t231eNZoGOCRkklbl4WI1kU= -github.com/database64128/tfo-go v1.1.2/go.mod h1:jgrSUPyOvTGQyn6irCOpk7L2W/q/0VLZZcovQiMi+bI= github.com/database64128/tfo-go/v2 v2.0.2 h1:5rGgkJeLEKlNaqredfrPQNLnctn1b+1fq/8tdKdOzJg= github.com/database64128/tfo-go/v2 v2.0.2/go.mod h1:FDdt4JaAsRU66wsYHxSVytYimPkKIHupVsxM+5DhvjY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From 1880a485f8c5234173f3b22a8607f8ada75afed8 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 16 Nov 2022 10:43:16 +0800 Subject: [PATCH 113/250] chore: better tfo inbound code --- adapter/inbound/listen.go | 26 ++++++++++++++++++++++++++ hub/executor/executor.go | 7 ++++--- hub/route/server.go | 4 ++-- listener/http/server.go | 14 +++++--------- listener/listener.go | 20 +++++--------------- listener/mixed/mixed.go | 10 +++------- listener/shadowsocks/tcp.go | 9 ++------- listener/sing_shadowsocks/server.go | 10 +++------- listener/sing_vmess/server.go | 8 ++------ listener/socks/tcp.go | 9 ++------- listener/tunnel/tcp.go | 2 +- 11 files changed, 55 insertions(+), 64 deletions(-) create mode 100644 adapter/inbound/listen.go diff --git a/adapter/inbound/listen.go b/adapter/inbound/listen.go new file mode 100644 index 00000000..d481a56e --- /dev/null +++ b/adapter/inbound/listen.go @@ -0,0 +1,26 @@ +package inbound + +import ( + "context" + "net" + + "github.com/database64128/tfo-go/v2" +) + +var ( + lc = tfo.ListenConfig{ + DisableTFO: true, + } +) + +func SetTfo(open bool) { + lc.DisableTFO = !open +} + +func ListenContext(ctx context.Context, network, address string) (net.Listener, error) { + return lc.Listen(ctx, network, address) +} + +func Listen(network, address string) (net.Listener, error) { + return ListenContext(context.Background(), network, address) +} diff --git a/hub/executor/executor.go b/hub/executor/executor.go index fad07136..4c77aaba 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -2,14 +2,13 @@ package executor import ( "fmt" - "github.com/Dreamacro/clash/component/tls" - "github.com/Dreamacro/clash/listener/inner" "net/netip" "os" "runtime" "sync" "github.com/Dreamacro/clash/adapter" + "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/adapter/outboundgroup" "github.com/Dreamacro/clash/component/auth" "github.com/Dreamacro/clash/component/dialer" @@ -19,6 +18,7 @@ import ( "github.com/Dreamacro/clash/component/profile/cachefile" "github.com/Dreamacro/clash/component/resolver" SNI "github.com/Dreamacro/clash/component/sniffer" + "github.com/Dreamacro/clash/component/tls" "github.com/Dreamacro/clash/component/trie" "github.com/Dreamacro/clash/config" C "github.com/Dreamacro/clash/constant" @@ -26,6 +26,7 @@ import ( "github.com/Dreamacro/clash/dns" P "github.com/Dreamacro/clash/listener" authStore "github.com/Dreamacro/clash/listener/auth" + "github.com/Dreamacro/clash/listener/inner" "github.com/Dreamacro/clash/listener/tproxy" "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/tunnel" @@ -335,7 +336,7 @@ func updateGeneral(general *config.General, force bool) { bindAddress := general.BindAddress P.SetBindAddress(bindAddress) - P.SetInboundTfo(general.InboundTfo) + inbound.SetTfo(general.InboundTfo) tcpIn := tunnel.TCPIn() udpIn := tunnel.UDPIn() diff --git a/hub/route/server.go b/hub/route/server.go index 62a8c95a..2d533496 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -3,11 +3,11 @@ package route import ( "bytes" "encoding/json" - "net" "net/http" "strings" "time" + "github.com/Dreamacro/clash/adapter/inbound" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/tunnel/statistic" @@ -85,7 +85,7 @@ func Start(addr string, secret string) { }) } - l, err := net.Listen("tcp", addr) + l, err := inbound.Listen("tcp", addr) if err != nil { log.Errorln("External controller listen error: %s", err) return diff --git a/listener/http/server.go b/listener/http/server.go index f507fac7..cf7948dd 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -1,10 +1,9 @@ package http import ( - "context" - "github.com/database64128/tfo-go/v2" "net" + "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/common/cache" C "github.com/Dreamacro/clash/constant" ) @@ -31,15 +30,12 @@ func (l *Listener) Close() error { return l.listener.Close() } -func New(addr string, inboundTfo bool, in chan<- C.ConnContext) (*Listener, error) { - return NewWithAuthenticate(addr, in, true, inboundTfo) +func New(addr string, in chan<- C.ConnContext) (*Listener, error) { + return NewWithAuthenticate(addr, in, true) } -func NewWithAuthenticate(addr string, in chan<- C.ConnContext, authenticate bool, inboundTfo bool) (*Listener, error) { - lc := tfo.ListenConfig{ - DisableTFO: !inboundTfo, - } - l, err := lc.Listen(context.Background(), "tcp", addr) +func NewWithAuthenticate(addr string, in chan<- C.ConnContext, authenticate bool) (*Listener, error) { + l, err := inbound.Listen("tcp", addr) if err != nil { return nil, err diff --git a/listener/listener.go b/listener/listener.go index ff7eb9d1..dfae99dc 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -14,7 +14,6 @@ import ( C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/listener/autoredir" "github.com/Dreamacro/clash/listener/http" - "github.com/Dreamacro/clash/listener/inner" "github.com/Dreamacro/clash/listener/mixed" "github.com/Dreamacro/clash/listener/redir" "github.com/Dreamacro/clash/listener/sing_shadowsocks" @@ -29,7 +28,6 @@ import ( var ( allowLan = false bindAddress = "*" - inboundTfo = false socksListener *socks.Listener socksUDPListener *socks.UDPListener @@ -103,14 +101,6 @@ func SetBindAddress(host string) { bindAddress = host } -func SetInboundTfo(itfo bool) { - inboundTfo = itfo -} - -func NewInner(tcpIn chan<- C.ConnContext) { - inner.New(tcpIn) -} - func ReCreateHTTP(port int, tcpIn chan<- C.ConnContext) { httpMux.Lock() defer httpMux.Unlock() @@ -136,7 +126,7 @@ func ReCreateHTTP(port int, tcpIn chan<- C.ConnContext) { return } - httpListener, err = http.New(addr, inboundTfo, tcpIn) + httpListener, err = http.New(addr, tcpIn) if err != nil { log.Errorln("Start HTTP server error: %s", err.Error()) return @@ -187,7 +177,7 @@ func ReCreateSocks(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P return } - tcpListener, err := socks.New(addr, inboundTfo, tcpIn) + tcpListener, err := socks.New(addr, tcpIn) if err != nil { return } @@ -280,7 +270,7 @@ func ReCreateShadowSocks(shadowSocksConfig string, tcpIn chan<- C.ConnContext, u return } - listener, err := sing_shadowsocks.New(shadowSocksConfig, inboundTfo, tcpIn, udpIn) + listener, err := sing_shadowsocks.New(shadowSocksConfig, tcpIn, udpIn) if err != nil { return } @@ -320,7 +310,7 @@ func ReCreateVmess(vmessConfig string, tcpIn chan<- C.ConnContext, udpIn chan<- return } - listener, err := sing_vmess.New(vmessConfig, inboundTfo, tcpIn, udpIn) + listener, err := sing_vmess.New(vmessConfig, tcpIn, udpIn) if err != nil { return } @@ -487,7 +477,7 @@ func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P return } - mixedListener, err = mixed.New(addr, inboundTfo, tcpIn) + mixedListener, err = mixed.New(addr, tcpIn) if err != nil { return } diff --git a/listener/mixed/mixed.go b/listener/mixed/mixed.go index 0fcaa80f..0ee50ba4 100644 --- a/listener/mixed/mixed.go +++ b/listener/mixed/mixed.go @@ -1,8 +1,7 @@ package mixed import ( - "context" - "github.com/database64128/tfo-go/v2" + "github.com/Dreamacro/clash/adapter/inbound" "net" "github.com/Dreamacro/clash/common/cache" @@ -38,11 +37,8 @@ func (l *Listener) Close() error { return l.listener.Close() } -func New(addr string, inboundTfo bool, in chan<- C.ConnContext) (*Listener, error) { - lc := tfo.ListenConfig{ - DisableTFO: !inboundTfo, - } - l, err := lc.Listen(context.Background(), "tcp", addr) +func New(addr string, in chan<- C.ConnContext) (*Listener, error) { + l, err := inbound.Listen("tcp", addr) if err != nil { return nil, err } diff --git a/listener/shadowsocks/tcp.go b/listener/shadowsocks/tcp.go index 37d29e61..c37892bb 100644 --- a/listener/shadowsocks/tcp.go +++ b/listener/shadowsocks/tcp.go @@ -1,8 +1,6 @@ package shadowsocks import ( - "context" - "github.com/database64128/tfo-go/v2" "net" "strings" @@ -23,7 +21,7 @@ type Listener struct { var _listener *Listener -func New(config string, inboundTfo bool, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (*Listener, error) { +func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (*Listener, error) { addr, cipher, password, err := ParseSSURL(config) if err != nil { return nil, err @@ -48,10 +46,7 @@ func New(config string, inboundTfo bool, tcpIn chan<- C.ConnContext, udpIn chan< sl.udpListeners = append(sl.udpListeners, ul) //TCP - lc := tfo.ListenConfig{ - DisableTFO: !inboundTfo, - } - l, err := lc.Listen(context.Background(), "tcp", addr) + l, err := inbound.Listen("tcp", addr) if err != nil { return nil, err } diff --git a/listener/sing_shadowsocks/server.go b/listener/sing_shadowsocks/server.go index bd500625..b7073e5d 100644 --- a/listener/sing_shadowsocks/server.go +++ b/listener/sing_shadowsocks/server.go @@ -3,7 +3,6 @@ package sing_shadowsocks import ( "context" "fmt" - "github.com/database64128/tfo-go/v2" "net" "strings" @@ -33,7 +32,7 @@ type Listener struct { var _listener *Listener -func New(config string, inboundTfo bool, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (C.AdvanceListener, error) { +func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (C.AdvanceListener, error) { addr, cipher, password, err := embedSS.ParseSSURL(config) if err != nil { return nil, err @@ -57,7 +56,7 @@ func New(config string, inboundTfo bool, tcpIn chan<- C.ConnContext, udpIn chan< sl.service, err = shadowaead_2022.NewServiceWithPassword(cipher, password, udpTimeout, h) default: err = fmt.Errorf("shadowsocks: unsupported method: %s", cipher) - return embedSS.New(config, inboundTfo, tcpIn, udpIn) + return embedSS.New(config, tcpIn, udpIn) } if err != nil { return nil, err @@ -101,10 +100,7 @@ func New(config string, inboundTfo bool, tcpIn chan<- C.ConnContext, udpIn chan< }() //TCP - lc := tfo.ListenConfig{ - DisableTFO: !inboundTfo, - } - l, err := lc.Listen(context.Background(), "tcp", addr) + l, err := inbound.Listen("tcp", addr) if err != nil { return nil, err } diff --git a/listener/sing_vmess/server.go b/listener/sing_vmess/server.go index f859d848..c02a2c79 100644 --- a/listener/sing_vmess/server.go +++ b/listener/sing_vmess/server.go @@ -2,7 +2,6 @@ package sing_vmess import ( "context" - "github.com/database64128/tfo-go/v2" "net" "net/url" "strings" @@ -25,7 +24,7 @@ type Listener struct { var _listener *Listener -func New(config string, inboundTfo bool, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (*Listener, error) { +func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (*Listener, error) { addr, username, password, err := parseVmessURL(config) if err != nil { return nil, err @@ -55,10 +54,7 @@ func New(config string, inboundTfo bool, tcpIn chan<- C.ConnContext, udpIn chan< addr := addr //TCP - lc := tfo.ListenConfig{ - DisableTFO: !inboundTfo, - } - l, err := lc.Listen(context.Background(), "tcp", addr) + l, err := inbound.Listen("tcp", addr) if err != nil { return nil, err } diff --git a/listener/socks/tcp.go b/listener/socks/tcp.go index 13a33d1b..8961c393 100644 --- a/listener/socks/tcp.go +++ b/listener/socks/tcp.go @@ -1,8 +1,6 @@ package socks import ( - "context" - "github.com/database64128/tfo-go/v2" "io" "net" @@ -36,11 +34,8 @@ func (l *Listener) Close() error { return l.listener.Close() } -func New(addr string, inboundTfo bool, in chan<- C.ConnContext) (*Listener, error) { - lc := tfo.ListenConfig{ - DisableTFO: !inboundTfo, - } - l, err := lc.Listen(context.Background(), "tcp", addr) +func New(addr string, in chan<- C.ConnContext) (*Listener, error) { + l, err := inbound.Listen("tcp", addr) if err != nil { return nil, err } diff --git a/listener/tunnel/tcp.go b/listener/tunnel/tcp.go index d51ec47a..3c591230 100644 --- a/listener/tunnel/tcp.go +++ b/listener/tunnel/tcp.go @@ -32,7 +32,7 @@ func New(config string, in chan<- C.ConnContext) (*Listener, error) { log.Errorln("invalid target address %q", target) return } - l, err := net.Listen("tcp", addr) + l, err := inbound.Listen("tcp", addr) if err != nil { return } From 994e85425f956e35baadff142eafdf898c0e48c3 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 16 Nov 2022 17:53:52 +0800 Subject: [PATCH 114/250] fix: resolver's defer --- dns/resolver.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dns/resolver.go b/dns/resolver.go index a0623dc2..4e31074c 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -159,9 +159,9 @@ func (r *Resolver) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, e continueFetch := false defer func() { if continueFetch || errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) { - ctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout) - defer cancel() go func() { + ctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout) + defer cancel() _, _ = r.exchangeWithoutCache(ctx, m) // ignore result, just for putMsgToCache }() } From 2dc62024fe5f399d80482e79c56a9e3553734159 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 16 Nov 2022 18:37:14 +0800 Subject: [PATCH 115/250] chore: support old chacha20 --- go.mod | 5 +++-- go.sum | 6 +++-- transport/shadowsocks/core/cipher.go | 1 + .../shadowsocks/shadowstream/old_chacha20.go | 22 +++++++++++++++++++ 4 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 transport/shadowsocks/shadowstream/old_chacha20.go diff --git a/go.mod b/go.mod index 8c70783e..140b1be0 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/Dreamacro/clash go 1.19 require ( + github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da github.com/cilium/ebpf v0.9.3 github.com/coreos/go-iptables v0.6.0 github.com/database64128/tfo-go/v2 v2.0.2 @@ -24,7 +25,7 @@ require ( github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4 github.com/sagernet/sing-shadowsocks v0.0.0-20221112030934-e55284e180ea - github.com/sagernet/sing-tun v0.0.0-20221012082254-488c3b75f6fd + github.com/sagernet/sing-tun v0.0.0-20221104121441-66c48a57776f github.com/sagernet/sing-vmess v0.0.0-20221109021549-b446d5bdddf0 github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c github.com/sirupsen/logrus v1.9.0 @@ -47,7 +48,7 @@ replace github.com/lucas-clemente/quic-go => github.com/HyNetwork/quic-go v0.30. replace github.com/sagernet/sing-tun => github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc -replace github.com/sagernet/sing-shadowsocks => github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221115121930-f1357ec9c15e +replace github.com/sagernet/sing-shadowsocks => github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221116103607-48a7095182b1 require ( github.com/ajg/form v1.5.1 // indirect diff --git a/go.sum b/go.sum index 96c25304..e85ef9b1 100644 --- a/go.sum +++ b/go.sum @@ -1,9 +1,11 @@ github.com/HyNetwork/quic-go v0.30.1-0.20221105180419-83715d7269a8 h1:FBo40lMrk1bZZzJRJx8U+bQUPhLDGTUJ/Q5NV5BbO4Q= github.com/HyNetwork/quic-go v0.30.1-0.20221105180419-83715d7269a8/go.mod h1:ssOrRsOmdxa768Wr78vnh2B8JozgLsMzG/g+0qEC7uk= -github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221115121930-f1357ec9c15e h1:61mKDq9ldYCH3Yb/Pelib+ISIXegprbm1CSPGB+HpT4= -github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221115121930-f1357ec9c15e/go.mod h1:16sNARQbsFbYIzAuPySszQA6Wfgzk7GWSzh1a6kDrUU= +github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221116103607-48a7095182b1 h1:guMcwJSIjk+jg/38uMPK5hIWVSaLHJ/l+ABZ8w2CKm0= +github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221116103607-48a7095182b1/go.mod h1:3bW+hVWFXOxRC1HL6CO6QHkegqjLohErGbcvt6dUN18= github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc h1:B1vmR4cwDjJQ6YM8NkuXmt9vIYOdV/+qA+F3UAFE3YY= github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc/go.mod h1:1u3pjXA9HmH7kRiBJqM3C/zPxrxnCLd3svmqtub/RFU= +github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= +github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= diff --git a/transport/shadowsocks/core/cipher.go b/transport/shadowsocks/core/cipher.go index 2f5acf63..7f4f7f71 100644 --- a/transport/shadowsocks/core/cipher.go +++ b/transport/shadowsocks/core/cipher.go @@ -59,6 +59,7 @@ var streamList = map[string]struct { "AES-128-CFB": {16, shadowstream.AESCFB}, "AES-192-CFB": {24, shadowstream.AESCFB}, "AES-256-CFB": {32, shadowstream.AESCFB}, + "CHACHA20": {32, shadowstream.ChaCha20}, "CHACHA20-IETF": {32, shadowstream.Chacha20IETF}, "XCHACHA20": {32, shadowstream.Xchacha20}, } diff --git a/transport/shadowsocks/shadowstream/old_chacha20.go b/transport/shadowsocks/shadowstream/old_chacha20.go new file mode 100644 index 00000000..65737fcc --- /dev/null +++ b/transport/shadowsocks/shadowstream/old_chacha20.go @@ -0,0 +1,22 @@ +package shadowstream + +import ( + "crypto/cipher" + "github.com/aead/chacha20/chacha" +) + +type chacha20key []byte + +func (k chacha20key) IVSize() int { + return chacha.NonceSize +} +func (k chacha20key) Encrypter(iv []byte) cipher.Stream { + c, _ := chacha.NewCipher(iv, k, 20) + return c +} +func (k chacha20key) Decrypter(iv []byte) cipher.Stream { + return k.Encrypter(iv) +} +func ChaCha20(key []byte) (Cipher, error) { + return chacha20key(key), nil +} From 8b848b62bba80bfc12b57ca2afd63dec5539a552 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 18 Nov 2022 18:02:46 +0800 Subject: [PATCH 116/250] fix: reset timeout in exchangeWithoutCache's singleflight --- dns/resolver.go | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/dns/resolver.go b/dns/resolver.go index 4e31074c..cb186dbe 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -187,7 +187,10 @@ func (r *Resolver) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, e func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { q := m.Question[0] - ret, err, shared := r.group.Do(q.String(), func() (result any, err error) { + ch := r.group.DoChan(q.String(), func() (result any, err error) { + ctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout) // reset timeout in singleflight + defer cancel() + defer func() { if err != nil { return @@ -209,6 +212,21 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M return r.batchExchange(ctx, r.main, m) }) + var result singleflight.Result + + select { + case result = <-ch: + break + case <-ctx.Done(): + select { + case result = <-ch: + break + } + return nil, ctx.Err() + } + + ret, err, shared := result.Val, result.Err, result.Shared + if err == nil { msg = ret.(*D.Msg) if shared { From dfc0ec995c93ed8c80de4071a722b96e1a2f23ca Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 18 Nov 2022 19:32:12 +0800 Subject: [PATCH 117/250] fix: wireguard handle conn is nil --- adapter/outbound/wireguard.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index eb48c0c0..33a0f44a 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -216,6 +216,9 @@ func (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata, opts if err != nil { return nil, err } + if conn == nil { + return nil, E.New("conn is nil") + } return NewConn(&wgConn{conn, w}, w), nil } From 16f8f77f5d70b1b5972302b75ff398bcb7bf7943 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 18 Nov 2022 19:40:39 +0800 Subject: [PATCH 118/250] fix: better wireguard error handle --- adapter/outbound/wireguard.go | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 33a0f44a..d08c0325 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -36,6 +36,7 @@ type WireGuard struct { tunDevice wireguard.Device dialer *wgDialer startOnce sync.Once + startErr error } type WireGuardOption struct { @@ -198,10 +199,10 @@ func (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata, opts w.dialer.options = opts var conn net.Conn w.startOnce.Do(func() { - err = w.tunDevice.Start() + w.startErr = w.tunDevice.Start() }) - if err != nil { - return nil, err + if w.startErr != nil { + return nil, w.startErr } if !metadata.Resolved() { var addrs []netip.Addr @@ -226,8 +227,11 @@ func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadat w.dialer.options = opts var pc net.PacketConn w.startOnce.Do(func() { - err = w.tunDevice.Start() + w.startErr = w.tunDevice.Start() }) + if w.startErr != nil { + return nil, w.startErr + } if err != nil { return nil, err } @@ -242,6 +246,9 @@ func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadat if err != nil { return nil, err } + if pc == nil { + return nil, E.New("packetConn is nil") + } return newPacketConn(&wgPacketConn{pc, w}, w), nil } From 23f286f24e11e1cc821dc9c42b2163a62614b73e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 19 Nov 2022 08:51:00 +0800 Subject: [PATCH 119/250] fix: auto retry in exchangeWithoutCache --- dns/resolver.go | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/dns/resolver.go b/dns/resolver.go index cb186dbe..4461a563 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -187,12 +187,16 @@ func (r *Resolver) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, e func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) { q := m.Question[0] - ch := r.group.DoChan(q.String(), func() (result any, err error) { + retryNum := 0 + retryMax := 3 + fn := func() (result any, err error) { ctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout) // reset timeout in singleflight defer cancel() defer func() { if err != nil { + result = retryNum + retryNum++ return } @@ -210,7 +214,9 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M return r.batchExchange(ctx, matched, m) } return r.batchExchange(ctx, r.main, m) - }) + } + + ch := r.group.DoChan(q.String(), fn) var result singleflight.Result @@ -219,13 +225,24 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M break case <-ctx.Done(): select { - case result = <-ch: + case result = <-ch: // maybe ctxDone and chFinish in same time, get DoChan's result as much as possible break + default: + go func() { // start a retrying monitor in background + result := <-ch + ret, err, shared := result.Val, result.Err, result.Shared + if err != nil && !shared && ret.(int) < retryMax { // retry + r.group.DoChan(q.String(), fn) + } + }() + return nil, ctx.Err() } - return nil, ctx.Err() } ret, err, shared := result.Val, result.Err, result.Shared + if err != nil && !shared && ret.(int) < retryMax { // retry + r.group.DoChan(q.String(), fn) + } if err == nil { msg = ret.(*D.Msg) From f00dc69bb6faae48119331248a0c4fdad12b4271 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 19 Nov 2022 09:43:31 +0800 Subject: [PATCH 120/250] fix: doh use NewRequestWithContext and batchExchange don't wait cancel finish --- dns/doh.go | 3 +-- dns/util.go | 23 ++++++++++++++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/dns/doh.go b/dns/doh.go index 8c38e2f4..f34246d5 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -208,14 +208,13 @@ func (doh *dnsOverHTTPS) exchangeHTTPSClient( } doh.url.RawQuery = fmt.Sprintf("dns=%s", base64.RawURLEncoding.EncodeToString(buf)) - httpReq, err := http.NewRequest(method, doh.url.String(), nil) + httpReq, err := http.NewRequestWithContext(ctx, method, doh.url.String(), nil) if err != nil { return nil, fmt.Errorf("creating http request to %s: %w", doh.url, err) } httpReq.Header.Set("Accept", "application/dns-message") httpReq.Header.Set("User-Agent", "") - _ = httpReq.WithContext(ctx) httpResp, err := client.Do(httpReq) if err != nil { return nil, fmt.Errorf("requesting %s: %w", doh.url, err) diff --git a/dns/util.go b/dns/util.go index 5bf09b8f..90693eae 100644 --- a/dns/util.go +++ b/dns/util.go @@ -216,7 +216,7 @@ func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.M fast, ctx := picker.WithTimeout[*D.Msg](ctx, resolver.DefaultDNSTimeout) for _, client := range clients { r := client - fast.Go(func() (*D.Msg, error) { + fn := func() (*D.Msg, error) { m, err := r.ExchangeContext(ctx, m) if err != nil { return nil, err @@ -224,6 +224,27 @@ func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.M return nil, errors.New("server failure") } return m, nil + } + fast.Go(func() (*D.Msg, error) { + ch := make(chan result, 1) + go func() { + m, err := fn() + ch <- result{ + Msg: m, + Error: err, + } + }() + select { + case r := <-ch: + return r.Msg, r.Error + case <-ctx.Done(): + select { + case r := <-ch: + return r.Msg, r.Error + default: + return nil, ctx.Err() + } + } }) } From b8b3c9ef9fcbd7df567bddda991f00f2bff2e938 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sat, 19 Nov 2022 10:31:50 +0800 Subject: [PATCH 121/250] fix: DoH/DoQ doesn't use context --- dns/doh.go | 63 ++++++++++++++++++++++++++---------------------------- dns/doq.go | 14 +++++------- 2 files changed, 35 insertions(+), 42 deletions(-) diff --git a/dns/doh.go b/dns/doh.go index f34246d5..84135b72 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -118,7 +118,7 @@ func (doh *dnsOverHTTPS) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D. // Check if there was already an active client before sending the request. // We'll only attempt to re-connect if there was one. - client, isCached, err := doh.getClient() + client, isCached, err := doh.getClient(ctx) if err != nil { return nil, fmt.Errorf("failed to init http client: %w", err) } @@ -132,7 +132,7 @@ func (doh *dnsOverHTTPS) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D. // the case when the connection was closed (due to inactivity for example) // AND the server refuses to open a 0-RTT connection. for i := 0; isCached && doh.shouldRetry(err) && i < 2; i++ { - client, err = doh.resetClient(err) + client, err = doh.resetClient(ctx, err) if err != nil { return nil, fmt.Errorf("failed to reset http client: %w", err) } @@ -142,7 +142,7 @@ func (doh *dnsOverHTTPS) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D. if err != nil { // If the request failed anyway, make sure we don't use this client. - _, resErr := doh.resetClient(err) + _, resErr := doh.resetClient(ctx, err) return nil, fmt.Errorf("err:%v,resErr:%v", err, resErr) } @@ -183,7 +183,6 @@ func (doh *dnsOverHTTPS) closeClient(client *http.Client) (err error) { // exchangeHTTPS logs the request and its result and calls exchangeHTTPSClient. func (doh *dnsOverHTTPS) exchangeHTTPS(ctx context.Context, client *http.Client, req *D.Msg) (resp *D.Msg, err error) { resp, err = doh.exchangeHTTPSClient(ctx, client, req) - return resp, err } @@ -207,23 +206,24 @@ func (doh *dnsOverHTTPS) exchangeHTTPSClient( method = http3.MethodGet0RTT } - doh.url.RawQuery = fmt.Sprintf("dns=%s", base64.RawURLEncoding.EncodeToString(buf)) - httpReq, err := http.NewRequestWithContext(ctx, method, doh.url.String(), nil) + url := doh.url + url.RawQuery = fmt.Sprintf("dns=%s", base64.RawURLEncoding.EncodeToString(buf)) + httpReq, err := http.NewRequestWithContext(ctx, method, url.String(), nil) if err != nil { - return nil, fmt.Errorf("creating http request to %s: %w", doh.url, err) + return nil, fmt.Errorf("creating http request to %s: %w", url, err) } httpReq.Header.Set("Accept", "application/dns-message") httpReq.Header.Set("User-Agent", "") httpResp, err := client.Do(httpReq) if err != nil { - return nil, fmt.Errorf("requesting %s: %w", doh.url, err) + return nil, fmt.Errorf("requesting %s: %w", url, err) } defer httpResp.Body.Close() body, err := io.ReadAll(httpResp.Body) if err != nil { - return nil, fmt.Errorf("reading %s: %w", doh.url, err) + return nil, fmt.Errorf("reading %s: %w", url, err) } if httpResp.StatusCode != http.StatusOK { @@ -232,7 +232,7 @@ func (doh *dnsOverHTTPS) exchangeHTTPSClient( "expected status %d, got %d from %s", http.StatusOK, httpResp.StatusCode, - doh.url, + url, ) } @@ -241,7 +241,7 @@ func (doh *dnsOverHTTPS) exchangeHTTPSClient( if err != nil { return nil, fmt.Errorf( "unpacking response from %s: body is %s: %w", - doh.url, + url, body, err, ) @@ -281,7 +281,7 @@ func (doh *dnsOverHTTPS) shouldRetry(err error) (ok bool) { // resetClient triggers re-creation of the *http.Client that is used by this // upstream. This method accepts the error that caused resetting client as // depending on the error we may also reset the QUIC config. -func (doh *dnsOverHTTPS) resetClient(resetErr error) (client *http.Client, err error) { +func (doh *dnsOverHTTPS) resetClient(ctx context.Context, resetErr error) (client *http.Client, err error) { doh.clientMu.Lock() defer doh.clientMu.Unlock() @@ -299,7 +299,7 @@ func (doh *dnsOverHTTPS) resetClient(resetErr error) (client *http.Client, err e } log.Debugln("re-creating the http client due to %v", resetErr) - doh.client, err = doh.createClient() + doh.client, err = doh.createClient(ctx) return doh.client, err } @@ -325,7 +325,7 @@ func (doh *dnsOverHTTPS) resetQUICConfig() { // getClient gets or lazily initializes an HTTP client (and transport) that will // be used for this DoH resolver. -func (doh *dnsOverHTTPS) getClient() (c *http.Client, isCached bool, err error) { +func (doh *dnsOverHTTPS) getClient(ctx context.Context) (c *http.Client, isCached bool, err error) { startTime := time.Now() doh.clientMu.Lock() @@ -342,7 +342,7 @@ func (doh *dnsOverHTTPS) getClient() (c *http.Client, isCached bool, err error) } log.Debugln("creating a new http client") - doh.client, err = doh.createClient() + doh.client, err = doh.createClient(ctx) return doh.client, false, err } @@ -351,8 +351,8 @@ func (doh *dnsOverHTTPS) getClient() (c *http.Client, isCached bool, err error) // will depend on whether HTTP3 is allowed and provided by this upstream. Note, // that we'll attempt to establish a QUIC connection when creating the client in // order to check whether HTTP3 is supported. -func (doh *dnsOverHTTPS) createClient() (*http.Client, error) { - transport, err := doh.createTransport() +func (doh *dnsOverHTTPS) createClient(ctx context.Context) (*http.Client, error) { + transport, err := doh.createTransport(ctx) if err != nil { return nil, fmt.Errorf("initializing http transport: %w", err) } @@ -374,7 +374,7 @@ func (doh *dnsOverHTTPS) createClient() (*http.Client, error) { // that this function will first attempt to establish a QUIC connection (if // HTTP3 is enabled in the upstream options). If this attempt is successful, // it returns an HTTP3 transport, otherwise it returns the H1/H2 transport. -func (doh *dnsOverHTTPS) createTransport() (t http.RoundTripper, err error) { +func (doh *dnsOverHTTPS) createTransport(ctx context.Context) (t http.RoundTripper, err error) { tlsConfig := tlsC.GetGlobalFingerprintTLCConfig( &tls.Config{ InsecureSkipVerify: false, @@ -390,7 +390,7 @@ func (doh *dnsOverHTTPS) createTransport() (t http.RoundTripper, err error) { // First, we attempt to create an HTTP3 transport. If the probe QUIC // connection is established successfully, we'll be using HTTP3 for this // upstream. - transportH3, err := doh.createTransportH3(tlsConfig, dialContext) + transportH3, err := doh.createTransportH3(ctx, tlsConfig, dialContext) if err == nil { log.Debugln("using HTTP/3 for this upstream: QUIC was faster") return transportH3, nil @@ -483,6 +483,7 @@ func (h *http3Transport) Close() (err error) { // in parallel (one for TLS, the other one for QUIC) and if QUIC is faster it // will create the *http3.RoundTripper instance. func (doh *dnsOverHTTPS) createTransportH3( + ctx context.Context, tlsConfig *tls.Config, dialContext dialHandler, ) (roundTripper http.RoundTripper, err error) { @@ -490,7 +491,7 @@ func (doh *dnsOverHTTPS) createTransportH3( return nil, errors.New("HTTP3 support is not enabled") } - addr, err := doh.probeH3(tlsConfig, dialContext) + addr, err := doh.probeH3(ctx, tlsConfig, dialContext) if err != nil { return nil, err } @@ -552,13 +553,14 @@ func (doh *dnsOverHTTPS) dialQuic(ctx context.Context, addr string, tlsCfg *tls. // upstream. If the test is successful it will return the address that we // should use to establish the QUIC connections. func (doh *dnsOverHTTPS) probeH3( + ctx context.Context, tlsConfig *tls.Config, dialContext dialHandler, ) (addr string, err error) { // We're using bootstrapped address instead of what's passed to the function // it does not create an actual connection, but it helps us determine // what IP is actually reachable (when there are v4/v6 addresses). - rawConn, err := dialContext(context.Background(), "udp", doh.url.Host) + rawConn, err := dialContext(ctx, "udp", doh.url.Host) if err != nil { return "", fmt.Errorf("failed to dial: %w", err) } @@ -597,8 +599,8 @@ func (doh *dnsOverHTTPS) probeH3( // Run probeQUIC and probeTLS in parallel and see which one is faster. chQuic := make(chan error, 1) chTLS := make(chan error, 1) - go doh.probeQUIC(addr, probeTLSCfg, chQuic) - go doh.probeTLS(dialContext, probeTLSCfg, chTLS) + go doh.probeQUIC(ctx, addr, probeTLSCfg, chQuic) + go doh.probeTLS(ctx, dialContext, probeTLSCfg, chTLS) select { case quicErr := <-chQuic: @@ -622,13 +624,8 @@ func (doh *dnsOverHTTPS) probeH3( // probeQUIC attempts to establish a QUIC connection to the specified address. // We run probeQUIC and probeTLS in parallel and see which one is faster. -func (doh *dnsOverHTTPS) probeQUIC(addr string, tlsConfig *tls.Config, ch chan error) { +func (doh *dnsOverHTTPS) probeQUIC(ctx context.Context, addr string, tlsConfig *tls.Config, ch chan error) { startTime := time.Now() - - timeout := DefaultTimeout - ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(timeout)) - defer cancel() - conn, err := doh.dialQuic(ctx, addr, tlsConfig, doh.getQUICConfig()) if err != nil { ch <- fmt.Errorf("opening QUIC connection to %s: %w", doh.Address(), err) @@ -646,10 +643,10 @@ func (doh *dnsOverHTTPS) probeQUIC(addr string, tlsConfig *tls.Config, ch chan e // probeTLS attempts to establish a TLS connection to the specified address. We // run probeQUIC and probeTLS in parallel and see which one is faster. -func (doh *dnsOverHTTPS) probeTLS(dialContext dialHandler, tlsConfig *tls.Config, ch chan error) { +func (doh *dnsOverHTTPS) probeTLS(ctx context.Context, dialContext dialHandler, tlsConfig *tls.Config, ch chan error) { startTime := time.Now() - conn, err := doh.tlsDial(dialContext, "tcp", tlsConfig) + conn, err := doh.tlsDial(ctx, dialContext, "tcp", tlsConfig) if err != nil { ch <- fmt.Errorf("opening TLS connection: %w", err) return @@ -705,10 +702,10 @@ func isHTTP3(client *http.Client) (ok bool) { // tlsDial is basically the same as tls.DialWithDialer, but we will call our own // dialContext function to get connection. -func (doh *dnsOverHTTPS) tlsDial(dialContext dialHandler, network string, config *tls.Config) (*tls.Conn, error) { +func (doh *dnsOverHTTPS) tlsDial(ctx context.Context, dialContext dialHandler, network string, config *tls.Config) (*tls.Conn, error) { // We're using bootstrapped address instead of what's passed // to the function. - rawConn, err := dialContext(context.Background(), network, doh.url.Host) + rawConn, err := dialContext(ctx, network, doh.url.Host) if err != nil { return nil, err } diff --git a/dns/doq.go b/dns/doq.go index 316417ef..7823a403 100644 --- a/dns/doq.go +++ b/dns/doq.go @@ -157,7 +157,7 @@ func (doq *dnsOverQUIC) Close() (err error) { // through it and return the response it got from the server. func (doq *dnsOverQUIC) exchangeQUIC(ctx context.Context, msg *D.Msg) (resp *D.Msg, err error) { var conn quic.Connection - conn, err = doq.getConnection(true) + conn, err = doq.getConnection(ctx,true) if err != nil { return nil, err } @@ -225,7 +225,7 @@ func (doq *dnsOverQUIC) getBytesPool() (pool *sync.Pool) { // argument controls whether we should try to use the existing cached // connection. If it is false, we will forcibly create a new connection and // close the existing one if needed. -func (doq *dnsOverQUIC) getConnection(useCached bool) (quic.Connection, error) { +func (doq *dnsOverQUIC) getConnection(ctx context.Context,useCached bool) (quic.Connection, error) { var conn quic.Connection doq.connMu.RLock() conn = doq.conn @@ -244,7 +244,7 @@ func (doq *dnsOverQUIC) getConnection(useCached bool) (quic.Connection, error) { defer doq.connMu.Unlock() var err error - conn, err = doq.openConnection() + conn, err = doq.openConnection(ctx) if err != nil { return nil, err } @@ -292,7 +292,7 @@ func (doq *dnsOverQUIC) openStream(ctx context.Context, conn quic.Connection) (q // We can get here if the old QUIC connection is not valid anymore. We // should try to re-create the connection again in this case. - newConn, err := doq.getConnection(false) + newConn, err := doq.getConnection(ctx,false) if err != nil { return nil, err } @@ -301,7 +301,7 @@ func (doq *dnsOverQUIC) openStream(ctx context.Context, conn quic.Connection) (q } // openConnection opens a new QUIC connection. -func (doq *dnsOverQUIC) openConnection() (conn quic.Connection, err error) { +func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connection, err error) { tlsConfig := tlsC.GetGlobalFingerprintTLCConfig( &tls.Config{ InsecureSkipVerify: false, @@ -313,14 +313,12 @@ func (doq *dnsOverQUIC) openConnection() (conn quic.Connection, err error) { // we're using bootstrapped address instead of what's passed to the function // it does not create an actual connection, but it helps us determine // what IP is actually reachable (when there're v4/v6 addresses). - ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout) rawConn, err := getDialHandler(doq.r, doq.proxyAdapter)(ctx, "udp", doq.addr) if err != nil { return nil, fmt.Errorf("failed to open a QUIC connection: %w", err) } // It's never actually used _ = rawConn.Close() - cancel() var addr string udpConn, ok := rawConn.(*net.UDPConn) if !ok { @@ -365,8 +363,6 @@ func (doq *dnsOverQUIC) openConnection() (conn quic.Connection, err error) { udp = wrapConn } - ctx, cancel = context.WithTimeout(context.Background(), DefaultTimeout) - defer cancel() host, _, err := net.SplitHostPort(doq.addr) if err != nil { return nil, err From 4ea422138060ddbbcf0f47507674816f3e4fb2e7 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 19 Nov 2022 10:35:45 +0800 Subject: [PATCH 122/250] fix: rollback batchExchange's code --- dns/util.go | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/dns/util.go b/dns/util.go index 90693eae..5bf09b8f 100644 --- a/dns/util.go +++ b/dns/util.go @@ -216,7 +216,7 @@ func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.M fast, ctx := picker.WithTimeout[*D.Msg](ctx, resolver.DefaultDNSTimeout) for _, client := range clients { r := client - fn := func() (*D.Msg, error) { + fast.Go(func() (*D.Msg, error) { m, err := r.ExchangeContext(ctx, m) if err != nil { return nil, err @@ -224,27 +224,6 @@ func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.M return nil, errors.New("server failure") } return m, nil - } - fast.Go(func() (*D.Msg, error) { - ch := make(chan result, 1) - go func() { - m, err := fn() - ch <- result{ - Msg: m, - Error: err, - } - }() - select { - case r := <-ch: - return r.Msg, r.Error - case <-ctx.Done(): - select { - case r := <-ch: - return r.Msg, r.Error - default: - return nil, ctx.Err() - } - } }) } From 7c1b878c3f4dd2183e1ca74f918a6a156c18e2d8 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 19 Nov 2022 10:45:44 +0800 Subject: [PATCH 123/250] fix: resolver's ctx --- component/resolver/resolver.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/component/resolver/resolver.go b/component/resolver/resolver.go index dfe47000..55f98d3c 100644 --- a/component/resolver/resolver.go +++ b/component/resolver/resolver.go @@ -70,8 +70,6 @@ func LookupIPv4WithResolver(ctx context.Context, host string, r Resolver) ([]net return DefaultResolver.LookupIPv4(ctx, host) } - ctx, cancel := context.WithTimeout(context.Background(), DefaultDNSTimeout) - defer cancel() ipAddrs, err := net.DefaultResolver.LookupNetIP(ctx, "ip4", host) if err != nil { return nil, err @@ -129,8 +127,6 @@ func LookupIPv6WithResolver(ctx context.Context, host string, r Resolver) ([]net return DefaultResolver.LookupIPv6(ctx, host) } - ctx, cancel := context.WithTimeout(context.Background(), DefaultDNSTimeout) - defer cancel() ipAddrs, err := net.DefaultResolver.LookupNetIP(ctx, "ip6", host) if err != nil { return nil, err From 8fcfecbed14c50dee1eca58788c641edb7de5aaa Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sat, 19 Nov 2022 10:45:22 +0800 Subject: [PATCH 124/250] chore: Adjust the return --- component/dialer/dialer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 330dcf81..4a6982ba 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -208,7 +208,7 @@ func dualStackDialContext(ctx context.Context, network, address string, opt *opt } } - return nil, errors.New("dual stack tcp shake hands failed") + return nil, ctx.Err() } func concurrentDualStackDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) { From 1d3cc36eefba842f30356e16ad6637b6ae002cf6 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sat, 19 Nov 2022 10:50:13 +0800 Subject: [PATCH 125/250] chore: return context error --- component/dialer/dialer.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 4a6982ba..42a037f9 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -331,6 +331,10 @@ func concurrentDialContext(ctx context.Context, network string, ips []netip.Addr return nil, fallback.error } + if ctx.Err() != nil { + return nil, ctx.Err() + } + return nil, fmt.Errorf("all ips %v tcp shake hands failed", ips) } From d5973cf8a6359dcf9aff3b8f80f15f692929bdf8 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sat, 19 Nov 2022 10:57:33 +0800 Subject: [PATCH 126/250] chore: Adjust error of dialer --- component/dialer/dialer.go | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 42a037f9..d3dc36ba 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -204,11 +204,17 @@ func dualStackDialContext(ctx context.Context, network, address string, opt *opt } } case <-ctx.Done(): + err=ctx.Err() break } } - return nil, ctx.Err() + if err==nil { + err=fmt.Errorf("dual stack dial failed") + }else{ + err=fmt.Errorf("dual stack dial failed:%w",err) + } + return nil, err } func concurrentDualStackDialContext(ctx context.Context, network, address string, opt *option) (net.Conn, error) { @@ -291,6 +297,7 @@ func concurrentDialContext(ctx context.Context, network string, ips []netip.Addr connCount := len(ips) var fallback dialResult var primaryError error + var finalError error for i := 0; i < connCount; i++ { select { case res := <-results: @@ -315,6 +322,7 @@ func concurrentDialContext(ctx context.Context, network string, ips []netip.Addr if fallback.done && fallback.error == nil { return fallback.Conn, nil } + finalError=ctx.Err() break } } @@ -331,11 +339,13 @@ func concurrentDialContext(ctx context.Context, network string, ips []netip.Addr return nil, fallback.error } - if ctx.Err() != nil { - return nil, ctx.Err() + if finalError==nil { + finalError=fmt.Errorf("all ips %v tcp shake hands failed", ips) + }else{ + finalError=fmt.Errorf("concurrent dial failed:%w",finalError) } - return nil, fmt.Errorf("all ips %v tcp shake hands failed", ips) + return nil, finalError } func singleDialContext(ctx context.Context, network string, address string, opt *option) (net.Conn, error) { From d3562ce3946ad9a38f652513dfc22fad4b42ebd4 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sat, 19 Nov 2022 22:48:04 +0800 Subject: [PATCH 127/250] fix: DoH recreate the connection multiple times --- dns/doh.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dns/doh.go b/dns/doh.go index 84135b72..6e3e8d88 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -12,6 +12,7 @@ import ( "net/url" "runtime" "strconv" + "strings" "sync" "time" @@ -140,7 +141,7 @@ func (doh *dnsOverHTTPS) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D. msg, err = doh.exchangeHTTPS(ctx, client, m) } - if err != nil { + if err != nil && !strings.Contains(err.Error(), "context canceled") { // If the request failed anyway, make sure we don't use this client. _, resErr := doh.resetClient(ctx, err) From dbbd499349a6c44567649ae09cf5213f56f05870 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 19 Nov 2022 23:03:14 +0800 Subject: [PATCH 128/250] fix: better error check --- dns/doh.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dns/doh.go b/dns/doh.go index 6e3e8d88..4d928bdc 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -12,7 +12,6 @@ import ( "net/url" "runtime" "strconv" - "strings" "sync" "time" @@ -141,7 +140,7 @@ func (doh *dnsOverHTTPS) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D. msg, err = doh.exchangeHTTPS(ctx, client, m) } - if err != nil && !strings.Contains(err.Error(), "context canceled") { + if err != nil && !errors.Is(err, context.Canceled) { // If the request failed anyway, make sure we don't use this client. _, resErr := doh.resetClient(ctx, err) From 6d89bddf2904944221eb5478091bfc44de4ee524 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 19 Nov 2022 23:06:27 +0800 Subject: [PATCH 129/250] fix: better error return --- dns/doh.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dns/doh.go b/dns/doh.go index 4d928bdc..02cf63e8 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -144,7 +144,7 @@ func (doh *dnsOverHTTPS) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D. // If the request failed anyway, make sure we don't use this client. _, resErr := doh.resetClient(ctx, err) - return nil, fmt.Errorf("err:%v,resErr:%v", err, resErr) + return nil, fmt.Errorf("%w (resErr:%v)", err, resErr) } return msg, err From 02830e0ad62e6bfa6467580d13858aa7e106e8a6 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sat, 19 Nov 2022 23:07:05 +0800 Subject: [PATCH 130/250] fix: adjust log --- dns/doh.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dns/doh.go b/dns/doh.go index 02cf63e8..413ede66 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -354,7 +354,7 @@ func (doh *dnsOverHTTPS) getClient(ctx context.Context) (c *http.Client, isCache func (doh *dnsOverHTTPS) createClient(ctx context.Context) (*http.Client, error) { transport, err := doh.createTransport(ctx) if err != nil { - return nil, fmt.Errorf("initializing http transport: %w", err) + return nil, fmt.Errorf("[%s] initializing http transport: %w", doh.url.String(), err) } client := &http.Client{ @@ -392,11 +392,11 @@ func (doh *dnsOverHTTPS) createTransport(ctx context.Context) (t http.RoundTripp // upstream. transportH3, err := doh.createTransportH3(ctx, tlsConfig, dialContext) if err == nil { - log.Debugln("using HTTP/3 for this upstream: QUIC was faster") + log.Debugln("[%s] using HTTP/3 for this upstream: QUIC was faster", doh.url.String()) return transportH3, nil } - log.Debugln("using HTTP/2 for this upstream: %v", err) + log.Debugln("[%s] using HTTP/2 for this upstream: %v", doh.url.String(), err) if !doh.supportsHTTP() { return nil, errors.New("HTTP1/1 and HTTP2 are not supported by this upstream") From 18d62c4a1710f7cf9c0e1c277881eeb6cf6bfb92 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 19 Nov 2022 23:16:20 +0800 Subject: [PATCH 131/250] fix: catch context.DeadlineExceeded too --- dns/doh.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dns/doh.go b/dns/doh.go index 413ede66..b5697168 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -140,7 +140,7 @@ func (doh *dnsOverHTTPS) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D. msg, err = doh.exchangeHTTPS(ctx, client, m) } - if err != nil && !errors.Is(err, context.Canceled) { + if err != nil && !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) { // If the request failed anyway, make sure we don't use this client. _, resErr := doh.resetClient(ctx, err) From 5b07d7b776d13cb91de7efafc648d51c73649218 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Fri, 18 Nov 2022 22:57:33 +0800 Subject: [PATCH 132/250] Feature: add tunnels --- config/config.go | 69 ++++++++++++++++++++++ constant/metadata.go | 20 ++++--- go.mod | 3 +- go.sum | 11 ++-- hub/executor/executor.go | 27 +++++---- listener/listener.go | 117 ++++++++++++++++++++++++++++++++++---- listener/tunnel/packet.go | 31 ++++++++++ listener/tunnel/tcp.go | 75 ++++++++++++++++++++++++ listener/tunnel/udp.go | 85 +++++++++++++++++++++++++++ tunnel/tunnel.go | 13 +++++ 10 files changed, 416 insertions(+), 35 deletions(-) create mode 100644 listener/tunnel/packet.go create mode 100644 listener/tunnel/tcp.go create mode 100644 listener/tunnel/udp.go diff --git a/config/config.go b/config/config.go index 8b1ed89c..d9063415 100644 --- a/config/config.go +++ b/config/config.go @@ -22,6 +22,7 @@ import ( R "github.com/Dreamacro/clash/rule" T "github.com/Dreamacro/clash/tunnel" + "github.com/samber/lo" "gopkg.in/yaml.v3" ) @@ -98,6 +99,7 @@ type Config struct { Users []auth.AuthUser Proxies map[string]C.Proxy Providers map[string]providerTypes.ProxyProvider + Tunnels []Tunnel } type RawDNS struct { @@ -122,6 +124,64 @@ type RawFallbackFilter struct { Domain []string `yaml:"domain"` } +type tunnel struct { + Network []string `yaml:"network"` + Address string `yaml:"address"` + Target string `yaml:"target"` + Proxy string `yaml:"proxy"` +} + +type Tunnel tunnel + +// UnmarshalYAML implements yaml.Unmarshaler +func (t *Tunnel) UnmarshalYAML(unmarshal func(any) error) error { + var tp string + if err := unmarshal(&tp); err != nil { + var inner tunnel + if err := unmarshal(&inner); err != nil { + return err + } + + *t = Tunnel(inner) + return nil + } + + // parse udp/tcp,address,target,proxy + parts := lo.Map(strings.Split(tp, ","), func(s string, _ int) string { + return strings.TrimSpace(s) + }) + if len(parts) != 4 { + return fmt.Errorf("invalid tunnel config %s", tp) + } + network := strings.Split(parts[0], "/") + + // validate network + for _, n := range network { + switch n { + case "tcp", "udp": + default: + return fmt.Errorf("invalid tunnel network %s", n) + } + } + + // validate address and target + address := parts[1] + target := parts[2] + for _, addr := range []string{address, target} { + if _, _, err := net.SplitHostPort(addr); err != nil { + return fmt.Errorf("invalid tunnel target or address %s", addr) + } + } + + *t = Tunnel(tunnel{ + Network: network, + Address: address, + Target: target, + Proxy: parts[3], + }) + return nil +} + type RawConfig struct { Port int `yaml:"port"` SocksPort int `yaml:"socks-port"` @@ -139,6 +199,7 @@ type RawConfig struct { Secret string `yaml:"secret"` Interface string `yaml:"interface-name"` RoutingMark int `yaml:"routing-mark"` + Tunnels []Tunnel `yaml:"tunnels"` ProxyProvider map[string]map[string]any `yaml:"proxy-providers"` Hosts map[string]string `yaml:"hosts"` @@ -237,6 +298,14 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { config.Users = parseAuthentication(rawCfg.Authentication) + config.Tunnels = rawCfg.Tunnels + // verify tunnels + for _, t := range config.Tunnels { + if _, ok := config.Proxies[t.Proxy]; !ok { + return nil, fmt.Errorf("tunnel proxy %s not found", t.Proxy) + } + } + return config, nil } diff --git a/constant/metadata.go b/constant/metadata.go index b829b7b0..cab6e37d 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -19,6 +19,7 @@ const ( SOCKS5 REDIR TPROXY + TUNNEL ) type NetWork int @@ -61,15 +62,16 @@ func (t Type) MarshalJSON() ([]byte, error) { // Metadata is used to store connection address type Metadata struct { - NetWork NetWork `json:"network"` - Type Type `json:"type"` - SrcIP net.IP `json:"sourceIP"` - DstIP net.IP `json:"destinationIP"` - SrcPort string `json:"sourcePort"` - DstPort string `json:"destinationPort"` - Host string `json:"host"` - DNSMode DNSMode `json:"dnsMode"` - ProcessPath string `json:"processPath"` + NetWork NetWork `json:"network"` + Type Type `json:"type"` + SrcIP net.IP `json:"sourceIP"` + DstIP net.IP `json:"destinationIP"` + SrcPort string `json:"sourcePort"` + DstPort string `json:"destinationPort"` + Host string `json:"host"` + DNSMode DNSMode `json:"dnsMode"` + ProcessPath string `json:"processPath"` + SpecialProxy string `json:"specialProxy"` } func (m *Metadata) RemoteAddress() string { diff --git a/go.mod b/go.mod index 25b2e1bd..66eef6b1 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/mdlayher/netlink v1.6.2 github.com/miekg/dns v1.1.50 github.com/oschwald/geoip2-golang v1.8.0 + github.com/samber/lo v1.35.0 github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.1 go.etcd.io/bbolt v1.3.6 @@ -29,11 +30,11 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/josharian/native v1.0.0 // indirect - github.com/kr/text v0.2.0 // indirect github.com/mdlayher/socket v0.2.3 // indirect github.com/oschwald/maxminddb-golang v1.10.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect + golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/text v0.4.0 // indirect golang.org/x/tools v0.1.12 // indirect diff --git a/go.sum b/go.sum index aac680fc..d92a0615 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,5 @@ github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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= @@ -34,9 +33,7 @@ github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGu github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y= github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= @@ -50,6 +47,7 @@ github.com/mdlayher/socket v0.2.3 h1:XZA2X2TjdOwNoNPVPclRCURoX/hokBY8nkTmRZFEheM github.com/mdlayher/socket v0.2.3/go.mod h1:bz12/FozYNH/VbvC3q7TRIK/Y6dH1kCKsXaUeXi/FmY= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs= github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw= github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg= @@ -57,6 +55,8 @@ github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYx github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/samber/lo v1.35.0 h1:GlT8CV1GE+v97Y7MLF1wXvX6mjoxZ+hi61tj/ZcQwY0= +github.com/samber/lo v1.35.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -70,6 +70,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA= github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= @@ -84,6 +85,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077 h1:t5bjOfJPQfaG9NV1imLZM5E2uzaLGs5/NtyMtRNVjQ4= golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= +golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -152,7 +155,7 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/hub/executor/executor.go b/hub/executor/executor.go index dc1f8601..a131f4b1 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -18,7 +18,7 @@ import ( C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/constant/provider" "github.com/Dreamacro/clash/dns" - P "github.com/Dreamacro/clash/listener" + "github.com/Dreamacro/clash/listener" authStore "github.com/Dreamacro/clash/listener/auth" "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/tunnel" @@ -75,10 +75,11 @@ func ApplyConfig(cfg *config.Config, force bool) { updateGeneral(cfg.General, force) updateDNS(cfg.DNS) updateExperimental(cfg) + updateTunnels(cfg.Tunnels) } func GetGeneral() *config.General { - ports := P.GetPorts() + ports := listener.GetPorts() authenticator := []string{} if auth := authStore.Authenticator(); auth != nil { authenticator = auth.Users() @@ -92,8 +93,8 @@ func GetGeneral() *config.General { TProxyPort: ports.TProxyPort, MixedPort: ports.MixedPort, Authentication: authenticator, - AllowLan: P.AllowLan(), - BindAddress: P.BindAddress(), + AllowLan: listener.AllowLan(), + BindAddress: listener.BindAddress(), }, Mode: tunnel.Mode(), LogLevel: log.Level(), @@ -161,6 +162,10 @@ func updateRules(rules []C.Rule) { tunnel.UpdateRules(rules) } +func updateTunnels(tunnels []config.Tunnel) { + listener.PatchTunnel(tunnels, tunnel.TCPIn(), tunnel.UDPIn()) +} + func updateGeneral(general *config.General, force bool) { log.SetLevel(general.LogLevel) tunnel.SetMode(general.Mode) @@ -176,19 +181,19 @@ func updateGeneral(general *config.General, force bool) { } allowLan := general.AllowLan - P.SetAllowLan(allowLan) + listener.SetAllowLan(allowLan) bindAddress := general.BindAddress - P.SetBindAddress(bindAddress) + listener.SetBindAddress(bindAddress) tcpIn := tunnel.TCPIn() udpIn := tunnel.UDPIn() - P.ReCreateHTTP(general.Port, tcpIn) - P.ReCreateSocks(general.SocksPort, tcpIn, udpIn) - P.ReCreateRedir(general.RedirPort, tcpIn, udpIn) - P.ReCreateTProxy(general.TProxyPort, tcpIn, udpIn) - P.ReCreateMixed(general.MixedPort, tcpIn, udpIn) + listener.ReCreateHTTP(general.Port, tcpIn) + listener.ReCreateSocks(general.SocksPort, tcpIn, udpIn) + listener.ReCreateRedir(general.RedirPort, tcpIn, udpIn) + listener.ReCreateTProxy(general.TProxyPort, tcpIn, udpIn) + listener.ReCreateMixed(general.MixedPort, tcpIn, udpIn) } func updateUsers(users []auth.AuthUser) { diff --git a/listener/listener.go b/listener/listener.go index b8e54b95..b5372327 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -1,34 +1,41 @@ -package proxy +package listener import ( "fmt" "net" "strconv" + "strings" "sync" "github.com/Dreamacro/clash/adapter/inbound" + "github.com/Dreamacro/clash/config" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/listener/http" "github.com/Dreamacro/clash/listener/mixed" "github.com/Dreamacro/clash/listener/redir" "github.com/Dreamacro/clash/listener/socks" "github.com/Dreamacro/clash/listener/tproxy" + "github.com/Dreamacro/clash/listener/tunnel" "github.com/Dreamacro/clash/log" + + "github.com/samber/lo" ) var ( allowLan = false bindAddress = "*" - socksListener *socks.Listener - socksUDPListener *socks.UDPListener - httpListener *http.Listener - redirListener *redir.Listener - redirUDPListener *tproxy.UDPListener - tproxyListener *tproxy.Listener - tproxyUDPListener *tproxy.UDPListener - mixedListener *mixed.Listener - mixedUDPLister *socks.UDPListener + socksListener *socks.Listener + socksUDPListener *socks.UDPListener + httpListener *http.Listener + redirListener *redir.Listener + redirUDPListener *tproxy.UDPListener + tproxyListener *tproxy.Listener + tproxyUDPListener *tproxy.UDPListener + mixedListener *mixed.Listener + mixedUDPLister *socks.UDPListener + tunnelTCPListeners = map[string]*tunnel.Listener{} + tunnelUDPListeners = map[string]*tunnel.PacketConn{} // lock for recreate function socksMux sync.Mutex @@ -36,6 +43,7 @@ var ( redirMux sync.Mutex tproxyMux sync.Mutex mixedMux sync.Mutex + tunnelMux sync.Mutex ) type Ports struct { @@ -301,6 +309,95 @@ func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P log.Infoln("Mixed(http+socks) proxy listening at: %s", mixedListener.Address()) } +func PatchTunnel(tunnels []config.Tunnel, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { + tunnelMux.Lock() + defer tunnelMux.Unlock() + + type addrProxy struct { + network string + addr string + target string + proxy string + } + + tcpOld := lo.Map( + lo.Keys(tunnelTCPListeners), + func(key string, _ int) addrProxy { + parts := strings.Split(key, "/") + return addrProxy{ + network: "tcp", + addr: parts[0], + target: parts[1], + proxy: parts[2], + } + }, + ) + udpOld := lo.Map( + lo.Keys(tunnelUDPListeners), + func(key string, _ int) addrProxy { + parts := strings.Split(key, "/") + return addrProxy{ + network: "udp", + addr: parts[0], + target: parts[1], + proxy: parts[2], + } + }, + ) + oldElm := lo.Union(tcpOld, udpOld) + + newElm := lo.FlatMap( + tunnels, + func(tunnel config.Tunnel, _ int) []addrProxy { + return lo.Map( + tunnel.Network, + func(network string, _ int) addrProxy { + return addrProxy{ + network: network, + addr: tunnel.Address, + target: tunnel.Target, + proxy: tunnel.Proxy, + } + }, + ) + }, + ) + + needClose, needCreate := lo.Difference(oldElm, newElm) + + for _, elm := range needClose { + key := fmt.Sprintf("%s/%s/%s", elm.addr, elm.target, elm.proxy) + if elm.network == "tcp" { + tunnelTCPListeners[key].Close() + delete(tunnelTCPListeners, key) + } else { + tunnelUDPListeners[key].Close() + delete(tunnelUDPListeners, key) + } + } + + for _, elm := range needCreate { + key := fmt.Sprintf("%s/%s/%s", elm.addr, elm.target, elm.proxy) + if elm.network == "tcp" { + l, err := tunnel.New(elm.addr, elm.target, elm.proxy, tcpIn) + if err != nil { + log.Errorln("Start tunnel %s error: %w", elm.target, err) + continue + } + tunnelTCPListeners[key] = l + log.Infoln("Tunnel(tcp/%s) proxy %s listening at: %s", elm.target, elm.proxy, tunnelTCPListeners[key].Address()) + } else { + l, err := tunnel.NewUDP(elm.addr, elm.target, elm.proxy, udpIn) + if err != nil { + log.Errorln("Start tunnel %s error: %w", elm.target, err) + continue + } + tunnelUDPListeners[key] = l + log.Infoln("Tunnel(udp/%s) proxy %s listening at: %s", elm.target, elm.proxy, tunnelUDPListeners[key].Address()) + } + } +} + // GetPorts return the ports of proxy servers func GetPorts() *Ports { ports := &Ports{} diff --git a/listener/tunnel/packet.go b/listener/tunnel/packet.go new file mode 100644 index 00000000..0ade9726 --- /dev/null +++ b/listener/tunnel/packet.go @@ -0,0 +1,31 @@ +package tunnel + +import ( + "net" + + "github.com/Dreamacro/clash/common/pool" +) + +type packet struct { + pc net.PacketConn + rAddr net.Addr + payload []byte +} + +func (c *packet) Data() []byte { + return c.payload +} + +// WriteBack write UDP packet with source(ip, port) = `addr` +func (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) { + return c.pc.WriteTo(b, c.rAddr) +} + +// LocalAddr returns the source IP/Port of UDP Packet +func (c *packet) LocalAddr() net.Addr { + return c.rAddr +} + +func (c *packet) Drop() { + pool.Put(c.payload) +} diff --git a/listener/tunnel/tcp.go b/listener/tunnel/tcp.go new file mode 100644 index 00000000..4ae5865c --- /dev/null +++ b/listener/tunnel/tcp.go @@ -0,0 +1,75 @@ +package tunnel + +import ( + "fmt" + "net" + + "github.com/Dreamacro/clash/adapter/inbound" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/transport/socks5" +) + +type Listener struct { + listener net.Listener + addr string + target socks5.Addr + proxy string + closed bool +} + +// RawAddress implements C.Listener +func (l *Listener) RawAddress() string { + return l.addr +} + +// Address implements C.Listener +func (l *Listener) Address() string { + return l.listener.Addr().String() +} + +// Close implements C.Listener +func (l *Listener) Close() error { + l.closed = true + return l.listener.Close() +} + +func (l *Listener) handleTCP(conn net.Conn, in chan<- C.ConnContext) { + conn.(*net.TCPConn).SetKeepAlive(true) + ctx := inbound.NewSocket(l.target, conn, C.TUNNEL) + ctx.Metadata().SpecialProxy = l.proxy + in <- ctx +} + +func New(addr, target, proxy string, in chan<- C.ConnContext) (*Listener, error) { + l, err := net.Listen("tcp", addr) + if err != nil { + return nil, err + } + + targetAddr := socks5.ParseAddr(target) + if targetAddr == nil { + return nil, fmt.Errorf("invalid target address %s", target) + } + + rl := &Listener{ + listener: l, + target: targetAddr, + proxy: proxy, + addr: addr, + } + + go func() { + for { + c, err := l.Accept() + if err != nil { + if rl.closed { + break + } + continue + } + go rl.handleTCP(c, in) + } + }() + + return rl, nil +} diff --git a/listener/tunnel/udp.go b/listener/tunnel/udp.go new file mode 100644 index 00000000..ee0ecbaf --- /dev/null +++ b/listener/tunnel/udp.go @@ -0,0 +1,85 @@ +package tunnel + +import ( + "fmt" + "net" + + "github.com/Dreamacro/clash/adapter/inbound" + "github.com/Dreamacro/clash/common/pool" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/transport/socks5" +) + +type PacketConn struct { + conn net.PacketConn + addr string + target socks5.Addr + proxy string + closed bool +} + +// RawAddress implements C.Listener +func (l *PacketConn) RawAddress() string { + return l.addr +} + +// Address implements C.Listener +func (l *PacketConn) Address() string { + return l.conn.LocalAddr().String() +} + +// Close implements C.Listener +func (l *PacketConn) Close() error { + l.closed = true + return l.conn.Close() +} + +func NewUDP(addr, target, proxy string, in chan<- *inbound.PacketAdapter) (*PacketConn, error) { + l, err := net.ListenPacket("udp", addr) + if err != nil { + return nil, err + } + + targetAddr := socks5.ParseAddr(target) + if targetAddr == nil { + return nil, fmt.Errorf("invalid target address %s", target) + } + + sl := &PacketConn{ + conn: l, + target: targetAddr, + proxy: proxy, + addr: addr, + } + go func() { + for { + buf := pool.Get(pool.UDPBufferSize) + n, remoteAddr, err := l.ReadFrom(buf) + if err != nil { + pool.Put(buf) + if sl.closed { + break + } + continue + } + sl.handleUDP(l, in, buf[:n], remoteAddr) + } + }() + + return sl, nil +} + +func (l *PacketConn) handleUDP(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buf []byte, addr net.Addr) { + packet := &packet{ + pc: pc, + rAddr: addr, + payload: buf, + } + + ctx := inbound.NewPacket(l.target, packet, C.TUNNEL) + ctx.Metadata().SpecialProxy = l.proxy + select { + case in <- ctx: + default: + } +} diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 0936056a..d659dd02 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -147,6 +147,15 @@ func preHandleMetadata(metadata *C.Metadata) error { } func resolveMetadata(ctx C.PlainContext, metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err error) { + if metadata.SpecialProxy != "" { + var exist bool + proxy, exist = proxies[metadata.SpecialProxy] + if !exist { + err = fmt.Errorf("proxy %s not found", metadata.SpecialProxy) + return + } + } + switch mode { case Direct: proxy = proxies["DIRECT"] @@ -249,6 +258,8 @@ func handleUDPConn(packet *inbound.PacketAdapter) { pc := statistic.NewUDPTracker(rawPc, statistic.DefaultManager, metadata, rule) switch true { + case metadata.SpecialProxy != "": + log.Infoln("[UDP] %s --> %s using %s", metadata.SourceAddress(), metadata.RemoteAddress(), metadata.SpecialProxy) case rule != nil: log.Infoln( "[UDP] %s --> %s match %s(%s) using %s", @@ -320,6 +331,8 @@ func handleTCPConn(connCtx C.ConnContext) { defer remoteConn.Close() switch true { + case metadata.SpecialProxy != "": + log.Infoln("[TCP] %s --> %s using %s", metadata.SourceAddress(), metadata.RemoteAddress(), metadata.SpecialProxy) case rule != nil: log.Infoln( "[TCP] %s --> %s match %s(%s) using %s", From d478728cb728bd7a3c540bd28ba596a14aae3b97 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 21 Nov 2022 10:33:42 +0800 Subject: [PATCH 133/250] fix: geosite match --- rules/common/geoip.go | 4 ++-- rules/common/geosite.go | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/rules/common/geoip.go b/rules/common/geoip.go index ada862d2..72e77045 100644 --- a/rules/common/geoip.go +++ b/rules/common/geoip.go @@ -2,10 +2,10 @@ package common import ( "fmt" - "github.com/Dreamacro/clash/component/geodata" - "github.com/Dreamacro/clash/component/geodata/router" "strings" + "github.com/Dreamacro/clash/component/geodata" + "github.com/Dreamacro/clash/component/geodata/router" "github.com/Dreamacro/clash/component/mmdb" "github.com/Dreamacro/clash/component/resolver" C "github.com/Dreamacro/clash/constant" diff --git a/rules/common/geosite.go b/rules/common/geosite.go index ad3cbae5..e5a0b9f7 100644 --- a/rules/common/geosite.go +++ b/rules/common/geosite.go @@ -2,13 +2,14 @@ package common import ( "fmt" + "github.com/Dreamacro/clash/component/geodata" _ "github.com/Dreamacro/clash/component/geodata/memconservative" "github.com/Dreamacro/clash/component/geodata/router" _ "github.com/Dreamacro/clash/component/geodata/standard" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" - _ "unsafe" + "github.com/Dreamacro/clash/transport/socks5" ) type GEOSITE struct { @@ -24,6 +25,10 @@ func (gs *GEOSITE) RuleType() C.RuleType { } func (gs *GEOSITE) Match(metadata *C.Metadata) (bool, string) { + if metadata.AddrType() != socks5.AtypDomainName { + return false, "" + } + domain := metadata.Host return gs.matcher.ApplyDomain(domain), gs.adapter } From 8c6e205c5a560f6179f3efdc05b17f984c077932 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Tue, 22 Nov 2022 19:16:08 +0800 Subject: [PATCH 134/250] Fix: tunnel proxy match --- tunnel/tunnel.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index d659dd02..bf09f23a 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -152,8 +152,8 @@ func resolveMetadata(ctx C.PlainContext, metadata *C.Metadata) (proxy C.Proxy, r proxy, exist = proxies[metadata.SpecialProxy] if !exist { err = fmt.Errorf("proxy %s not found", metadata.SpecialProxy) - return } + return } switch mode { From efa4b9e0b8ac8aaf1c97bcfd48224d0ce8869139 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Tue, 22 Nov 2022 21:01:51 +0800 Subject: [PATCH 135/250] Fix: lint warning --- listener/listener.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/listener/listener.go b/listener/listener.go index b5372327..972ca2d2 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -381,7 +381,7 @@ func PatchTunnel(tunnels []config.Tunnel, tcpIn chan<- C.ConnContext, udpIn chan if elm.network == "tcp" { l, err := tunnel.New(elm.addr, elm.target, elm.proxy, tcpIn) if err != nil { - log.Errorln("Start tunnel %s error: %w", elm.target, err) + log.Errorln("Start tunnel %s error: %s", elm.target, err.Error()) continue } tunnelTCPListeners[key] = l @@ -389,7 +389,7 @@ func PatchTunnel(tunnels []config.Tunnel, tcpIn chan<- C.ConnContext, udpIn chan } else { l, err := tunnel.NewUDP(elm.addr, elm.target, elm.proxy, udpIn) if err != nil { - log.Errorln("Start tunnel %s error: %w", elm.target, err) + log.Errorln("Start tunnel %s error: %s", elm.target, err.Error()) continue } tunnelUDPListeners[key] = l From 3880c3c1be0f97f0f2985329f45a3c720ba58cd7 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 24 Nov 2022 12:32:35 +0800 Subject: [PATCH 136/250] chore: add retry in tunnel dial --- go.mod | 1 + go.sum | 2 ++ tunnel/tunnel.go | 42 ++++++++++++++++++++++++++++++++++++++---- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 140b1be0..feef8810 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/gorilla/websocket v1.5.0 github.com/hashicorp/golang-lru v0.5.4 github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c + github.com/jpillora/backoff v1.0.0 github.com/lucas-clemente/quic-go v0.29.1 github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/mdlayher/netlink v1.1.1 diff --git a/go.sum b/go.sum index e85ef9b1..fe36da98 100644 --- a/go.sum +++ b/go.sum @@ -62,6 +62,8 @@ github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Go github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c h1:OCFM4+DXTWfNlyeoddrTwdup/ztkGSyAMR2UGcPckNQ= github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 2e38da40..c602a0be 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -11,6 +11,8 @@ import ( "sync" "time" + "github.com/jpillora/backoff" + "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/component/nat" P "github.com/Dreamacro/clash/component/process" @@ -273,8 +275,9 @@ func handleUDPConn(packet *inbound.PacketAdapter) { ctx, cancel := context.WithTimeout(context.Background(), C.DefaultUDPTimeout) defer cancel() - rawPc, err := proxy.ListenPacketContext(ctx, metadata.Pure()) - if err != nil { + rawPc, err := retry(ctx, func(ctx context.Context) (C.PacketConn, error) { + return proxy.ListenPacketContext(ctx, metadata.Pure()) + }, func(err error) { if rule == nil { log.Warnln( "[UDP] dial %s %s --> %s error: %s", @@ -286,6 +289,8 @@ func handleUDPConn(packet *inbound.PacketAdapter) { } else { log.Warnln("[UDP] dial %s (match %s/%s) %s --> %s error: %s", proxy.Name(), rule.RuleType().String(), rule.Payload(), metadata.SourceAddress(), metadata.RemoteAddress(), err.Error()) } + }) + if err != nil { return } pCtx.InjectPacketConn(rawPc) @@ -354,8 +359,9 @@ func handleTCPConn(connCtx C.ConnContext) { ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTCPTimeout) defer cancel() - remoteConn, err := proxy.DialContext(ctx, dialMetadata) - if err != nil { + remoteConn, err := retry(ctx, func(ctx context.Context) (C.Conn, error) { + return proxy.DialContext(ctx, dialMetadata) + }, func(err error) { if rule == nil { log.Warnln( "[TCP] dial %s %s --> %s error: %s", @@ -367,6 +373,8 @@ func handleTCPConn(connCtx C.ConnContext) { } else { log.Warnln("[TCP] dial %s (match %s/%s) %s --> %s error: %s", proxy.Name(), rule.RuleType().String(), rule.Payload(), metadata.SourceAddress(), metadata.RemoteAddress(), err.Error()) } + }) + if err != nil { return } @@ -473,3 +481,29 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { return proxies["DIRECT"], nil, nil } + +func retry[T any](ctx context.Context, ft func(context.Context) (T, error), fe func(err error)) (t T, err error) { + b := &backoff.Backoff{ + Min: 10 * time.Millisecond, + Max: 1 * time.Second, + Factor: 2, + Jitter: true, + } + for i := 0; i < 10; i++ { + t, err = ft(ctx) + if err != nil { + if fe != nil { + fe(err) + } + select { + case <-time.After(b.Duration()): + continue + case <-ctx.Done(): + return + } + } else { + break + } + } + return +} From 59bd11a3a784b99d72bbaeac716d3b6d7a53a2ab Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 25 Nov 2022 08:08:14 +0800 Subject: [PATCH 137/250] chore: add tuic outbound close #133 --- README.md | 19 ++ adapter/outbound/tuic.go | 216 +++++++++++++++++ adapter/parser.go | 13 +- component/dialer/dialer.go | 28 ++- constant/adapters.go | 3 + docs/config.yaml | 14 ++ go.mod | 2 +- transport/tuic/client.go | 377 ++++++++++++++++++++++++++++++ transport/tuic/protocol.go | 468 +++++++++++++++++++++++++++++++++++++ 9 files changed, 1125 insertions(+), 15 deletions(-) create mode 100644 adapter/outbound/tuic.go create mode 100644 transport/tuic/client.go create mode 100644 transport/tuic/protocol.go diff --git a/README.md b/README.md index ce16182d..4a3fc3b5 100644 --- a/README.md +++ b/README.md @@ -227,6 +227,25 @@ proxies: udp: true ``` +Support outbound transport protocol `Tuic` +```yaml +proxies: + - name: "tuic" + server: www.example.com + port: 10443 + type: tuic + token: TOKEN + # ip: 127.0.0.1 + # heartbeat_interval: 10000 + # alpn: [h3] + # disable_sni: true + reduce_rtt: true +# request_timeout: 8000 + udp_relay_mode: native + # skip-cert-verify: true + +``` + ### IPTABLES configuration Work on Linux OS who's supported `iptables` diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go new file mode 100644 index 00000000..43843fdb --- /dev/null +++ b/adapter/outbound/tuic.go @@ -0,0 +1,216 @@ +package outbound + +import ( + "context" + "crypto/sha256" + "crypto/tls" + "encoding/hex" + "encoding/pem" + "fmt" + "net" + "os" + "strconv" + "sync" + "time" + + "github.com/lucas-clemente/quic-go" + + "github.com/Dreamacro/clash/component/dialer" + tlsC "github.com/Dreamacro/clash/component/tls" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/transport/tuic" +) + +type Tuic struct { + *Base + option *TuicOption + getClient func(opts ...dialer.Option) *tuic.Client +} + +type TuicOption struct { + BasicOption + Name string `proxy:"name"` + Server string `proxy:"server"` + Port int `proxy:"port"` + Token string `proxy:"token"` + Ip string `proxy:"ip,omitempty"` + HeartbeatInterval int `proxy:"heartbeat_interval,omitempty"` + ALPN []string `proxy:"alpn,omitempty"` + ReduceRtt bool `proxy:"reduce_rtt,omitempty"` + RequestTimeout int `proxy:"request_timeout,omitempty"` + UdpRelayMode string `proxy:"udp_relay_mode,omitempty"` + DisableSni bool `proxy:"disable_sni,omitempty"` + + SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + Fingerprint string `proxy:"fingerprint,omitempty"` + CustomCA string `proxy:"ca,omitempty"` + CustomCAString string `proxy:"ca_str,omitempty"` + ReceiveWindowConn int `proxy:"recv_window_conn,omitempty"` + ReceiveWindow int `proxy:"recv_window,omitempty"` + DisableMTUDiscovery bool `proxy:"disable_mtu_discovery,omitempty"` +} + +// DialContext implements C.ProxyAdapter +func (t *Tuic) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { + opts = t.Base.DialOptions(opts...) + conn, err := t.getClient(opts...).DialContext(ctx, metadata, func(ctx context.Context) (net.PacketConn, net.Addr, error) { + pc, err := dialer.ListenPacket(ctx, "udp", "", opts...) + if err != nil { + return nil, nil, err + } + addr, err := resolveUDPAddrWithPrefer(ctx, "udp", t.addr, t.prefer) + if err != nil { + return nil, nil, err + } + return pc, addr, err + }) + if err != nil { + return nil, err + } + return NewConn(conn, t), err +} + +// ListenPacketContext implements C.ProxyAdapter +func (t *Tuic) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { + opts = t.Base.DialOptions(opts...) + pc, err := t.getClient(opts...).ListenPacketContext(ctx, metadata, func(ctx context.Context) (net.PacketConn, net.Addr, error) { + pc, err := dialer.ListenPacket(ctx, "udp", "", opts...) + if err != nil { + return nil, nil, err + } + addr, err := resolveUDPAddrWithPrefer(ctx, "udp", t.addr, t.prefer) + if err != nil { + return nil, nil, err + } + return pc, addr, err + }) + if err != nil { + return nil, err + } + return newPacketConn(pc, t), nil +} + +func NewTuic(option TuicOption) (*Tuic, error) { + addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) + serverName := option.Server + + tlsConfig := &tls.Config{ + ServerName: serverName, + InsecureSkipVerify: option.SkipCertVerify, + MinVersion: tls.VersionTLS13, + } + + var bs []byte + var err error + if len(option.CustomCA) > 0 { + bs, err = os.ReadFile(option.CustomCA) + if err != nil { + return nil, fmt.Errorf("hysteria %s load ca error: %w", addr, err) + } + } else if option.CustomCAString != "" { + bs = []byte(option.CustomCAString) + } + + if len(bs) > 0 { + block, _ := pem.Decode(bs) + if block == nil { + return nil, fmt.Errorf("CA cert is not PEM") + } + + fpBytes := sha256.Sum256(block.Bytes) + if len(option.Fingerprint) == 0 { + option.Fingerprint = hex.EncodeToString(fpBytes[:]) + } + } + + if len(option.Fingerprint) != 0 { + var err error + tlsConfig, err = tlsC.GetSpecifiedFingerprintTLSConfig(tlsConfig, option.Fingerprint) + if err != nil { + return nil, err + } + } else { + tlsConfig = tlsC.GetGlobalFingerprintTLCConfig(tlsConfig) + } + + if len(option.ALPN) > 0 { + tlsConfig.NextProtos = option.ALPN + } else { + tlsConfig.NextProtos = []string{"h3"} + } + + if option.RequestTimeout == 0 { + option.RequestTimeout = 8000 + } + + if option.HeartbeatInterval <= 0 { + option.HeartbeatInterval = 10000 + } + + if option.UdpRelayMode != "quic" { + option.UdpRelayMode = "native" + } + + quicConfig := &quic.Config{ + InitialStreamReceiveWindow: uint64(option.ReceiveWindowConn), + MaxStreamReceiveWindow: uint64(option.ReceiveWindowConn), + InitialConnectionReceiveWindow: uint64(option.ReceiveWindow), + MaxConnectionReceiveWindow: uint64(option.ReceiveWindow), + KeepAlivePeriod: time.Duration(option.HeartbeatInterval) * time.Millisecond, + DisablePathMTUDiscovery: option.DisableMTUDiscovery, + EnableDatagrams: true, + } + if option.ReceiveWindowConn == 0 { + quicConfig.InitialStreamReceiveWindow = DefaultStreamReceiveWindow / 10 + quicConfig.MaxStreamReceiveWindow = DefaultStreamReceiveWindow + } + if option.ReceiveWindow == 0 { + quicConfig.InitialConnectionReceiveWindow = DefaultConnectionReceiveWindow / 10 + quicConfig.MaxConnectionReceiveWindow = DefaultConnectionReceiveWindow + } + + if len(option.Ip) > 0 { + addr = net.JoinHostPort(option.Ip, strconv.Itoa(option.Port)) + } + host := option.Server + if option.DisableSni { + host = "" + tlsConfig.ServerName = "" + } + tkn := tuic.GenTKN(option.Token) + clientMap := make(map[any]*tuic.Client) + clientMapMutex := sync.Mutex{} + getClient := func(opts ...dialer.Option) *tuic.Client { + o := *dialer.ApplyOptions(opts...) + + clientMapMutex.Lock() + defer clientMapMutex.Unlock() + if client, ok := clientMap[o]; ok && client != nil { + return client + } + client := &tuic.Client{ + TlsConfig: tlsConfig, + QuicConfig: quicConfig, + Host: host, + Token: tkn, + UdpRelayMode: option.UdpRelayMode, + ReduceRtt: option.ReduceRtt, + RequestTimeout: option.RequestTimeout, + } + clientMap[o] = client + return client + } + + return &Tuic{ + Base: &Base{ + name: option.Name, + addr: addr, + tp: C.Tuic, + udp: true, + iface: option.Interface, + prefer: C.NewDNSPrefer(option.IPVersion), + }, + option: &option, + getClient: getClient, + }, nil +} diff --git a/adapter/parser.go b/adapter/parser.go index 5d145998..0ce054f8 100644 --- a/adapter/parser.go +++ b/adapter/parser.go @@ -89,12 +89,19 @@ func ParseProxy(mapping map[string]any) (C.Proxy, error) { } proxy, err = outbound.NewHysteria(*hyOption) case "wireguard": - hyOption := &outbound.WireGuardOption{} - err = decoder.Decode(mapping, hyOption) + wgOption := &outbound.WireGuardOption{} + err = decoder.Decode(mapping, wgOption) if err != nil { break } - proxy, err = outbound.NewWireGuard(*hyOption) + proxy, err = outbound.NewWireGuard(*wgOption) + case "tuic": + tuicOption := &outbound.TuicOption{} + err = decoder.Decode(mapping, tuicOption) + if err != nil { + break + } + proxy, err = outbound.NewTuic(*tuicOption) default: return nil, fmt.Errorf("unsupport proxy type: %s", proxyType) } diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index d3dc36ba..cb87061c 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -22,7 +22,7 @@ var ( ErrorDisableIPv6 = errors.New("IPv6 is disabled, dialer cancel") ) -func DialContext(ctx context.Context, network, address string, options ...Option) (net.Conn, error) { +func ApplyOptions(options ...Option) *option { opt := &option{ interfaceName: DefaultInterface.Load(), routingMark: int(DefaultRoutingMark.Load()), @@ -36,6 +36,12 @@ func DialContext(ctx context.Context, network, address string, options ...Option o(opt) } + return opt +} + +func DialContext(ctx context.Context, network, address string, options ...Option) (net.Conn, error) { + opt := ApplyOptions(options...) + if opt.network == 4 || opt.network == 6 { if strings.Contains(network, "tcp") { network = "tcp" @@ -204,15 +210,15 @@ func dualStackDialContext(ctx context.Context, network, address string, opt *opt } } case <-ctx.Done(): - err=ctx.Err() + err = ctx.Err() break } } - if err==nil { - err=fmt.Errorf("dual stack dial failed") - }else{ - err=fmt.Errorf("dual stack dial failed:%w",err) + if err == nil { + err = fmt.Errorf("dual stack dial failed") + } else { + err = fmt.Errorf("dual stack dial failed:%w", err) } return nil, err } @@ -322,7 +328,7 @@ func concurrentDialContext(ctx context.Context, network string, ips []netip.Addr if fallback.done && fallback.error == nil { return fallback.Conn, nil } - finalError=ctx.Err() + finalError = ctx.Err() break } } @@ -339,10 +345,10 @@ func concurrentDialContext(ctx context.Context, network string, ips []netip.Addr return nil, fallback.error } - if finalError==nil { - finalError=fmt.Errorf("all ips %v tcp shake hands failed", ips) - }else{ - finalError=fmt.Errorf("concurrent dial failed:%w",finalError) + if finalError == nil { + finalError = fmt.Errorf("all ips %v tcp shake hands failed", ips) + } else { + finalError = fmt.Errorf("concurrent dial failed:%w", finalError) } return nil, finalError diff --git a/constant/adapters.go b/constant/adapters.go index 47826a74..53d03fb0 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -32,6 +32,7 @@ const ( Trojan Hysteria WireGuard + Tuic ) const ( @@ -168,6 +169,8 @@ func (at AdapterType) String() string { return "Hysteria" case WireGuard: return "WireGuard" + case Tuic: + return "Tuic" case Relay: return "Relay" diff --git a/docs/config.yaml b/docs/config.yaml index 36050e28..39ed60be 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -458,6 +458,20 @@ proxies: public-key: Cr8hWlKvtDt7nrvf+f0brNQQzabAqrjfBvas9pmowjo= udp: true + - name: tuic + server: www.example.com + port: 10443 + type: tuic + token: TOKEN + # ip: 127.0.0.1 + # heartbeat_interval: 10000 + # alpn: [h3] + # disable_sni: true + reduce_rtt: true +# request_timeout: 8000 + udp_relay_mode: native + # skip-cert-verify: true + # ShadowsocksR # The supported ciphers (encryption methods): all stream ciphers in ss # The supported obfses: diff --git a/go.mod b/go.mod index feef8810..6a06a917 100644 --- a/go.mod +++ b/go.mod @@ -42,6 +42,7 @@ require ( golang.org/x/sys v0.2.0 google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v3 v3.0.1 + lukechampine.com/blake3 v1.1.7 ) @@ -75,5 +76,4 @@ require ( golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect golang.org/x/tools v0.1.12 // indirect gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c // indirect - lukechampine.com/blake3 v1.1.7 // indirect ) diff --git a/transport/tuic/client.go b/transport/tuic/client.go new file mode 100644 index 00000000..3d9506c1 --- /dev/null +++ b/transport/tuic/client.go @@ -0,0 +1,377 @@ +package tuic + +import ( + "bufio" + "bytes" + "context" + "crypto/tls" + "errors" + "math/rand" + "net" + "net/netip" + "sync" + "time" + + "github.com/lucas-clemente/quic-go" + + N "github.com/Dreamacro/clash/common/net" + C "github.com/Dreamacro/clash/constant" +) + +type Client struct { + TlsConfig *tls.Config + QuicConfig *quic.Config + Host string + Token [32]byte + UdpRelayMode string + ReduceRtt bool + RequestTimeout int + + quicConn quic.Connection + connMutex sync.Mutex + + udpInputMap sync.Map +} + +func (t *Client) getQuicConn(ctx context.Context, dialFn func(ctx context.Context) (net.PacketConn, net.Addr, error)) (quic.Connection, error) { + t.connMutex.Lock() + defer t.connMutex.Unlock() + if t.quicConn != nil { + return t.quicConn, nil + } + pc, addr, err := dialFn(ctx) + if err != nil { + return nil, err + } + var quicConn quic.Connection + if t.ReduceRtt { + quicConn, err = quic.DialEarlyContext(ctx, pc, addr, t.Host, t.TlsConfig, t.QuicConfig) + } else { + quicConn, err = quic.DialContext(ctx, pc, addr, t.Host, t.TlsConfig, t.QuicConfig) + } + if err != nil { + return nil, err + } + + sendAuthentication := func(quicConn quic.Connection) (err error) { + defer func() { + t.deferQuicConn(quicConn, err) + }() + stream, err := quicConn.OpenUniStream() + if err != nil { + return err + } + buf := &bytes.Buffer{} + err = NewAuthenticate(t.Token).WriteTo(buf) + if err != nil { + return err + } + _, err = buf.WriteTo(stream) + if err != nil { + return err + } + err = stream.Close() + if err != nil { + return + } + return nil + } + + go sendAuthentication(quicConn) + + go func(quicConn quic.Connection) (err error) { + defer func() { + t.deferQuicConn(quicConn, err) + }() + switch t.UdpRelayMode { + case "quic": + for { + var stream quic.ReceiveStream + stream, err = quicConn.AcceptUniStream(context.Background()) + if err != nil { + return err + } + go func() (err error) { + var assocId uint32 + defer func() { + t.deferQuicConn(quicConn, err) + if err != nil && assocId != 0 { + if val, ok := t.udpInputMap.LoadAndDelete(assocId); ok { + if conn, ok := val.(net.Conn); ok { + _ = conn.Close() + } + } + } + }() + reader := bufio.NewReader(stream) + packet, err := ReadPacket(reader) + if err != nil { + return + } + assocId = packet.ASSOC_ID + if val, ok := t.udpInputMap.Load(assocId); ok { + if conn, ok := val.(net.Conn); ok { + writer := bufio.NewWriterSize(conn, packet.BytesLen()) + _ = packet.WriteTo(writer) + _ = writer.Flush() + } + } + return + }() + } + default: // native + for { + var message []byte + message, err = quicConn.ReceiveMessage() + if err != nil { + return err + } + go func() (err error) { + var assocId uint32 + defer func() { + t.deferQuicConn(quicConn, err) + if err != nil && assocId != 0 { + if val, ok := t.udpInputMap.LoadAndDelete(assocId); ok { + if conn, ok := val.(net.Conn); ok { + _ = conn.Close() + } + } + } + }() + buffer := bytes.NewBuffer(message) + packet, err := ReadPacket(buffer) + if err != nil { + return + } + assocId = packet.ASSOC_ID + if val, ok := t.udpInputMap.Load(assocId); ok { + if conn, ok := val.(net.Conn); ok { + _, _ = conn.Write(message) + } + } + return + }() + } + } + }(quicConn) + + t.quicConn = quicConn + return quicConn, nil +} + +func (t *Client) deferQuicConn(quicConn quic.Connection, err error) { + var netError net.Error + if err != nil && errors.As(err, &netError) { + t.connMutex.Lock() + defer t.connMutex.Unlock() + if t.quicConn == quicConn { + t.udpInputMap.Range(func(key, value any) bool { + if conn, ok := value.(net.Conn); ok { + _ = conn.Close() + } + return true + }) + t.udpInputMap = sync.Map{} // new one + t.quicConn = nil + } + } +} + +func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata, dialFn func(ctx context.Context) (net.PacketConn, net.Addr, error)) (net.Conn, error) { + quicConn, err := t.getQuicConn(ctx, dialFn) + if err != nil { + return nil, err + } + defer func() { + t.deferQuicConn(quicConn, err) + }() + buf := &bytes.Buffer{} + err = NewConnect(NewAddress(metadata)).WriteTo(buf) + if err != nil { + return nil, err + } + stream, err := quicConn.OpenStream() + if err != nil { + return nil, err + } + _, err = buf.WriteTo(stream) + if err != nil { + return nil, err + } + if t.RequestTimeout > 0 { + _ = stream.SetReadDeadline(time.Now().Add(time.Duration(t.RequestTimeout) * time.Millisecond)) + } + conn := N.NewBufferedConn(&quicStreamConn{stream, quicConn.LocalAddr(), quicConn.RemoteAddr()}) + response, err := ReadResponse(conn) + if err != nil { + return nil, err + } + if response.IsFailed() { + _ = stream.Close() + return nil, errors.New("connect failed") + } + _ = stream.SetReadDeadline(time.Time{}) + return conn, err +} + +type quicStreamConn struct { + quic.Stream + lAddr net.Addr + rAddr net.Addr +} + +func (q *quicStreamConn) LocalAddr() net.Addr { + return q.lAddr +} + +func (q *quicStreamConn) RemoteAddr() net.Addr { + return q.rAddr +} + +var _ net.Conn = &quicStreamConn{} + +func (t *Client) ListenPacketContext(ctx context.Context, metadata *C.Metadata, dialFn func(ctx context.Context) (net.PacketConn, net.Addr, error)) (net.PacketConn, error) { + quicConn, err := t.getQuicConn(ctx, dialFn) + if err != nil { + return nil, err + } + + pipe1, pipe2 := net.Pipe() + inputCh := make(chan udpData) + var connId uint32 + for { + connId = rand.Uint32() + _, loaded := t.udpInputMap.LoadOrStore(connId, pipe1) + if !loaded { + break + } + } + pc := &quicStreamPacketConn{ + connId: connId, + quicConn: quicConn, + lAddr: quicConn.LocalAddr(), + client: t, + inputConn: N.NewBufferedConn(pipe2), + inputCh: inputCh, + } + return pc, nil +} + +type udpData struct { + data []byte + addr net.Addr + err error +} + +type quicStreamPacketConn struct { + connId uint32 + quicConn quic.Connection + lAddr net.Addr + client *Client + inputConn *N.BufferedConn + inputCh chan udpData + + closeOnce sync.Once + closeErr error +} + +func (q *quicStreamPacketConn) Close() error { + q.closeOnce.Do(func() { + q.closeErr = q.close() + }) + return q.closeErr +} + +func (q *quicStreamPacketConn) close() (err error) { + defer func() { + q.client.deferQuicConn(q.quicConn, err) + }() + buf := &bytes.Buffer{} + err = NewDissociate(q.connId).WriteTo(buf) + if err != nil { + return + } + stream, err := q.quicConn.OpenUniStream() + if err != nil { + return + } + _, err = buf.WriteTo(stream) + if err != nil { + return + } + err = stream.Close() + if err != nil { + return + } + return +} + +func (q *quicStreamPacketConn) SetDeadline(t time.Time) error { + //TODO implement me + return nil +} + +func (q *quicStreamPacketConn) SetReadDeadline(t time.Time) error { + return q.inputConn.SetReadDeadline(t) +} + +func (q *quicStreamPacketConn) SetWriteDeadline(t time.Time) error { + //TODO implement me + return nil +} + +func (q *quicStreamPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + packet, err := ReadPacket(q.inputConn) + if err != nil { + return + } + n = copy(p, packet.DATA) + addr = packet.ADDR.UDPAddr() + return +} + +func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + defer func() { + q.client.deferQuicConn(q.quicConn, err) + }() + addr.String() + buf := &bytes.Buffer{} + addrPort, err := netip.ParseAddrPort(addr.String()) + if err != nil { + return + } + err = NewPacket(q.connId, uint16(len(p)), NewAddressAddrPort(addrPort), p).WriteTo(buf) + if err != nil { + return + } + switch q.client.UdpRelayMode { + case "quic": + var stream quic.SendStream + stream, err = q.quicConn.OpenUniStream() + if err != nil { + return + } + _, err = buf.WriteTo(stream) + if err != nil { + return + } + err = stream.Close() + if err != nil { + return + } + default: // native + err = q.quicConn.SendMessage(buf.Bytes()) + if err != nil { + return + } + } + n = len(p) + + return +} + +func (q *quicStreamPacketConn) LocalAddr() net.Addr { + return q.lAddr +} + +var _ net.PacketConn = &quicStreamPacketConn{} diff --git a/transport/tuic/protocol.go b/transport/tuic/protocol.go new file mode 100644 index 00000000..b3b000b4 --- /dev/null +++ b/transport/tuic/protocol.go @@ -0,0 +1,468 @@ +package tuic + +import ( + "encoding/binary" + "fmt" + "io" + "net" + "net/netip" + "strconv" + + "github.com/lucas-clemente/quic-go" + "lukechampine.com/blake3" + + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/transport/socks5" +) + +type BufferedReader interface { + io.Reader + io.ByteReader +} + +type BufferedWriter interface { + io.Writer + io.ByteWriter +} + +type CommandType byte + +const ( + AuthenticateType = CommandType(0x00) + ConnectType = CommandType(0x01) + PacketType = CommandType(0x02) + DissociateType = CommandType(0x03) + HeartbeatType = CommandType(0x04) + ResponseType = CommandType(0x05) +) + +func (c CommandType) String() string { + switch c { + case AuthenticateType: + return "Authenticate" + case ConnectType: + return "Connect" + case PacketType: + return "Packet" + case DissociateType: + return "Dissociate" + case HeartbeatType: + return "Heartbeat" + case ResponseType: + return "Response" + default: + return fmt.Sprintf("UnknowCommand: %#x", byte(c)) + } +} + +func (c CommandType) BytesLen() int { + return 1 +} + +type CommandHead struct { + VER byte + TYPE CommandType +} + +func NewCommandHead(TYPE CommandType) CommandHead { + return CommandHead{ + VER: 0x04, + TYPE: TYPE, + } +} + +func ReadCommandHead(reader BufferedReader) (c CommandHead, err error) { + c.VER, err = reader.ReadByte() + if err != nil { + return + } + TYPE, err := reader.ReadByte() + if err != nil { + return + } + c.TYPE = CommandType(TYPE) + return +} + +func (c CommandHead) WriteTo(writer BufferedWriter) (err error) { + err = writer.WriteByte(c.VER) + if err != nil { + return + } + err = writer.WriteByte(byte(c.TYPE)) + if err != nil { + return + } + return +} + +func (c CommandHead) BytesLen() int { + return 1 + c.TYPE.BytesLen() +} + +type Authenticate struct { + CommandHead + TKN [32]byte +} + +func NewAuthenticate(TKN [32]byte) Authenticate { + return Authenticate{ + CommandHead: NewCommandHead(AuthenticateType), + TKN: TKN, + } +} + +func GenTKN(token string) [32]byte { + return blake3.Sum256([]byte(token)) +} + +func (c Authenticate) WriteTo(writer BufferedWriter) (err error) { + err = c.CommandHead.WriteTo(writer) + if err != nil { + return + } + _, err = writer.Write(c.TKN[:]) + if err != nil { + return + } + return +} + +func (c Authenticate) BytesLen() int { + return c.CommandHead.BytesLen() + 32 +} + +type Connect struct { + CommandHead + ADDR Address +} + +func NewConnect(ADDR Address) Connect { + return Connect{ + CommandHead: NewCommandHead(ConnectType), + ADDR: ADDR, + } +} + +func (c Connect) WriteTo(writer BufferedWriter) (err error) { + err = c.CommandHead.WriteTo(writer) + if err != nil { + return + } + err = c.ADDR.WriteTo(writer) + if err != nil { + return + } + return +} + +func (c Connect) BytesLen() int { + return c.CommandHead.BytesLen() + c.ADDR.BytesLen() +} + +type Packet struct { + CommandHead + ASSOC_ID uint32 + LEN uint16 + ADDR Address + DATA []byte +} + +func NewPacket(ASSOC_ID uint32, LEN uint16, ADDR Address, DATA []byte) Packet { + return Packet{ + CommandHead: NewCommandHead(PacketType), + ASSOC_ID: ASSOC_ID, + LEN: LEN, + ADDR: ADDR, + DATA: DATA, + } +} + +func ReadPacket(reader BufferedReader) (c Packet, err error) { + c.CommandHead, err = ReadCommandHead(reader) + if err != nil { + return + } + if c.CommandHead.TYPE != PacketType { + err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE) + } + err = binary.Read(reader, binary.BigEndian, &c.ASSOC_ID) + if err != nil { + return + } + err = binary.Read(reader, binary.BigEndian, &c.LEN) + if err != nil { + return + } + c.ADDR, err = ReadAddress(reader) + if err != nil { + return + } + c.DATA = make([]byte, c.LEN) + _, err = io.ReadFull(reader, c.DATA) + if err != nil { + return + } + return +} + +func (c Packet) WriteTo(writer BufferedWriter) (err error) { + err = c.CommandHead.WriteTo(writer) + if err != nil { + return + } + err = binary.Write(writer, binary.BigEndian, c.ASSOC_ID) + if err != nil { + return + } + err = binary.Write(writer, binary.BigEndian, c.LEN) + if err != nil { + return + } + err = c.ADDR.WriteTo(writer) + if err != nil { + return + } + _, err = writer.Write(c.DATA) + if err != nil { + return + } + return +} + +func (c Packet) BytesLen() int { + return c.CommandHead.BytesLen() + 4 + 2 + c.ADDR.BytesLen() + len(c.DATA) +} + +type Dissociate struct { + CommandHead + ASSOC_ID uint32 +} + +func NewDissociate(ASSOC_ID uint32) Dissociate { + return Dissociate{ + CommandHead: NewCommandHead(DissociateType), + ASSOC_ID: ASSOC_ID, + } +} + +func (c Dissociate) WriteTo(writer BufferedWriter) (err error) { + err = c.CommandHead.WriteTo(writer) + if err != nil { + return + } + err = binary.Write(writer, binary.BigEndian, c.ASSOC_ID) + if err != nil { + return + } + return +} + +func (c Dissociate) BytesLen() int { + return c.CommandHead.BytesLen() + 4 +} + +type Heartbeat struct { + CommandHead +} + +func NewHeartbeat() Heartbeat { + return Heartbeat{ + CommandHead: NewCommandHead(HeartbeatType), + } +} + +func ReadHeartbeat(reader BufferedReader) (c Response, err error) { + c.CommandHead, err = ReadCommandHead(reader) + if err != nil { + return + } + if c.CommandHead.TYPE != HeartbeatType { + err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE) + } + return +} + +type Response struct { + CommandHead + REP byte +} + +func NewResponse(REP byte) Response { + return Response{ + CommandHead: NewCommandHead(ResponseType), + REP: REP, + } +} + +func ReadResponse(reader BufferedReader) (c Response, err error) { + c.CommandHead, err = ReadCommandHead(reader) + if err != nil { + return + } + if c.CommandHead.TYPE != ResponseType { + err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE) + } + c.REP, err = reader.ReadByte() + if err != nil { + return + } + return +} + +func (c Response) WriteTo(writer BufferedWriter) (err error) { + err = c.CommandHead.WriteTo(writer) + if err != nil { + return + } + err = writer.WriteByte(c.REP) + if err != nil { + return + } + return +} + +func (c Response) IsSucceed() bool { + return c.REP == 0x00 +} + +func (c Response) IsFailed() bool { + return c.REP == 0xff +} + +func (c Response) BytesLen() int { + return c.CommandHead.BytesLen() + 1 +} + +// Addr types +const ( + AtypDomainName byte = 0 + AtypIPv4 byte = 1 + AtypIPv6 byte = 2 +) + +type Address struct { + TYPE byte + ADDR []byte + PORT uint16 +} + +func NewAddress(metadata *C.Metadata) Address { + var addrType byte + var addr []byte + switch metadata.AddrType() { + case socks5.AtypIPv4: + addrType = AtypIPv4 + addr = make([]byte, net.IPv4len) + copy(addr[:], metadata.DstIP.AsSlice()) + case socks5.AtypIPv6: + addrType = AtypIPv6 + addr = make([]byte, net.IPv6len) + copy(addr[:], metadata.DstIP.AsSlice()) + case socks5.AtypDomainName: + addrType = AtypDomainName + addr = make([]byte, len(metadata.Host)+1) + addr[0] = byte(len(metadata.Host)) + copy(addr[1:], metadata.Host) + } + + port, _ := strconv.ParseUint(metadata.DstPort, 10, 16) + + return Address{ + TYPE: addrType, + ADDR: addr, + PORT: uint16(port), + } +} + +func NewAddressAddrPort(addrPort netip.AddrPort) Address { + var addrType byte + var addr []byte + if addrPort.Addr().Is4() { + addrType = AtypIPv4 + addr = make([]byte, net.IPv4len) + } else { + addrType = AtypIPv6 + addr = make([]byte, net.IPv6len) + } + copy(addr[:], addrPort.Addr().AsSlice()) + return Address{ + TYPE: addrType, + ADDR: addr, + PORT: addrPort.Port(), + } +} + +func ReadAddress(reader BufferedReader) (c Address, err error) { + c.TYPE, err = reader.ReadByte() + if err != nil { + return + } + switch c.TYPE { + case AtypIPv4: + c.ADDR = make([]byte, net.IPv4len) + _, err = io.ReadFull(reader, c.ADDR) + if err != nil { + return + } + case AtypIPv6: + c.ADDR = make([]byte, net.IPv6len) + _, err = io.ReadFull(reader, c.ADDR) + if err != nil { + return + } + case AtypDomainName: + var addrLen byte + addrLen, err = reader.ReadByte() + if err != nil { + return + } + c.ADDR = make([]byte, addrLen+1) + c.ADDR[0] = addrLen + _, err = io.ReadFull(reader, c.ADDR[1:]) + if err != nil { + return + } + } + + err = binary.Read(reader, binary.BigEndian, &c.PORT) + if err != nil { + return + } + return +} + +func (c Address) WriteTo(writer BufferedWriter) (err error) { + err = writer.WriteByte(c.TYPE) + if err != nil { + return + } + _, err = writer.Write(c.ADDR[:]) + if err != nil { + return + } + err = binary.Write(writer, binary.BigEndian, c.PORT) + if err != nil { + return + } + return +} + +func (c Address) UDPAddr() *net.UDPAddr { + return &net.UDPAddr{ + IP: c.ADDR, + Port: int(c.PORT), + Zone: "", + } +} + +func (c Address) BytesLen() int { + return 1 + len(c.ADDR) + 2 +} + +const ( + ProtocolError = quic.ApplicationErrorCode(0xfffffff0) + AuthenticationFailed = quic.ApplicationErrorCode(0xfffffff1) + AuthenticationTimeout = quic.ApplicationErrorCode(0xfffffff2) + BadCommand = quic.ApplicationErrorCode(0xfffffff3) +) From c89b1f0e96ee64383de569a5f22c67f03c840273 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 25 Nov 2022 10:33:37 +0800 Subject: [PATCH 138/250] chore: tuic add cubic,new_reno,bbr congestion_controller --- README.md | 5 +- adapter/outbound/hysteria.go | 4 +- adapter/outbound/tuic.go | 42 +- dns/doh.go | 4 +- dns/doq.go | 12 +- docs/config.yaml | 5 +- go.mod | 5 +- go.sum | 4 +- test/go.mod | 30 +- test/go.sum | 251 +---- transport/hysteria/congestion/brutal.go | 2 +- transport/hysteria/congestion/pacer.go | 2 +- transport/hysteria/core/client.go | 4 +- transport/hysteria/core/stream.go | 2 +- transport/hysteria/transport/client.go | 2 +- transport/tuic/client.go | 48 +- transport/tuic/congestion/bandwidth.go | 25 + .../tuic/congestion/bandwidth_sampler.go | 376 +++++++ transport/tuic/congestion/bbr_sender.go | 992 ++++++++++++++++++ transport/tuic/congestion/clock.go | 18 + transport/tuic/congestion/cubic.go | 213 ++++ transport/tuic/congestion/cubic_sender.go | 318 ++++++ .../tuic/congestion/hybrid_slow_start.go | 112 ++ transport/tuic/congestion/minmax.go | 72 ++ transport/tuic/congestion/pacer.go | 79 ++ transport/tuic/congestion/windowed_filter.go | 132 +++ transport/tuic/protocol.go | 2 +- 27 files changed, 2465 insertions(+), 296 deletions(-) create mode 100644 transport/tuic/congestion/bandwidth.go create mode 100644 transport/tuic/congestion/bandwidth_sampler.go create mode 100644 transport/tuic/congestion/bbr_sender.go create mode 100644 transport/tuic/congestion/clock.go create mode 100644 transport/tuic/congestion/cubic.go create mode 100644 transport/tuic/congestion/cubic_sender.go create mode 100644 transport/tuic/congestion/hybrid_slow_start.go create mode 100644 transport/tuic/congestion/minmax.go create mode 100644 transport/tuic/congestion/pacer.go create mode 100644 transport/tuic/congestion/windowed_filter.go diff --git a/README.md b/README.md index 4a3fc3b5..417c6ac7 100644 --- a/README.md +++ b/README.md @@ -240,8 +240,9 @@ proxies: # alpn: [h3] # disable_sni: true reduce_rtt: true -# request_timeout: 8000 - udp_relay_mode: native + # request_timeout: 8000 + udp_relay_mode: native # Available: "native", "quic". Default: "native" + # congestion_controller: bbr # Available: "cubic", "new_reno", "bbr". Default: "cubic" # skip-cert-verify: true ``` diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index 4fa40f3a..5a2850c6 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -14,8 +14,8 @@ import ( "strconv" "time" - "github.com/lucas-clemente/quic-go" - "github.com/lucas-clemente/quic-go/congestion" + "github.com/metacubex/quic-go" + "github.com/metacubex/quic-go/congestion" M "github.com/sagernet/sing/common/metadata" "github.com/Dreamacro/clash/component/dialer" diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index 43843fdb..b4d17653 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -13,7 +13,7 @@ import ( "sync" "time" - "github.com/lucas-clemente/quic-go" + "github.com/metacubex/quic-go" "github.com/Dreamacro/clash/component/dialer" tlsC "github.com/Dreamacro/clash/component/tls" @@ -23,23 +23,23 @@ import ( type Tuic struct { *Base - option *TuicOption getClient func(opts ...dialer.Option) *tuic.Client } type TuicOption struct { BasicOption - Name string `proxy:"name"` - Server string `proxy:"server"` - Port int `proxy:"port"` - Token string `proxy:"token"` - Ip string `proxy:"ip,omitempty"` - HeartbeatInterval int `proxy:"heartbeat_interval,omitempty"` - ALPN []string `proxy:"alpn,omitempty"` - ReduceRtt bool `proxy:"reduce_rtt,omitempty"` - RequestTimeout int `proxy:"request_timeout,omitempty"` - UdpRelayMode string `proxy:"udp_relay_mode,omitempty"` - DisableSni bool `proxy:"disable_sni,omitempty"` + Name string `proxy:"name"` + Server string `proxy:"server"` + Port int `proxy:"port"` + Token string `proxy:"token"` + Ip string `proxy:"ip,omitempty"` + HeartbeatInterval int `proxy:"heartbeat_interval,omitempty"` + ALPN []string `proxy:"alpn,omitempty"` + ReduceRtt bool `proxy:"reduce_rtt,omitempty"` + RequestTimeout int `proxy:"request_timeout,omitempty"` + UdpRelayMode string `proxy:"udp_relay_mode,omitempty"` + CongestionController string `proxy:"congestion_controller,omitempty"` + DisableSni bool `proxy:"disable_sni,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` Fingerprint string `proxy:"fingerprint,omitempty"` @@ -189,13 +189,14 @@ func NewTuic(option TuicOption) (*Tuic, error) { return client } client := &tuic.Client{ - TlsConfig: tlsConfig, - QuicConfig: quicConfig, - Host: host, - Token: tkn, - UdpRelayMode: option.UdpRelayMode, - ReduceRtt: option.ReduceRtt, - RequestTimeout: option.RequestTimeout, + TlsConfig: tlsConfig, + QuicConfig: quicConfig, + Host: host, + Token: tkn, + UdpRelayMode: option.UdpRelayMode, + CongestionController: option.CongestionController, + ReduceRtt: option.ReduceRtt, + RequestTimeout: option.RequestTimeout, } clientMap[o] = client return client @@ -210,7 +211,6 @@ func NewTuic(option TuicOption) (*Tuic, error) { iface: option.Interface, prefer: C.NewDNSPrefer(option.IPVersion), }, - option: &option, getClient: getClient, }, nil } diff --git a/dns/doh.go b/dns/doh.go index b5697168..fc32a212 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -19,8 +19,8 @@ import ( tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" - "github.com/lucas-clemente/quic-go" - "github.com/lucas-clemente/quic-go/http3" + "github.com/metacubex/quic-go" + "github.com/metacubex/quic-go/http3" D "github.com/miekg/dns" "golang.org/x/net/http2" ) diff --git a/dns/doq.go b/dns/doq.go index 7823a403..d4fbb037 100644 --- a/dns/doq.go +++ b/dns/doq.go @@ -15,7 +15,7 @@ import ( "github.com/Dreamacro/clash/component/dialer" tlsC "github.com/Dreamacro/clash/component/tls" - "github.com/lucas-clemente/quic-go" + "github.com/metacubex/quic-go" "github.com/Dreamacro/clash/log" D "github.com/miekg/dns" @@ -34,7 +34,7 @@ const ( // controls the period with with keep-alive frames are being sent to the // connection. We set it to 20s as it would be in the quic-go@v0.27.1 with // KeepAlive field set to true This value is specified in - // https://pkg.go.dev/github.com/lucas-clemente/quic-go/internal/protocol#MaxKeepAliveInterval. + // https://pkg.go.dev/github.com/metacubex/quic-go/internal/protocol#MaxKeepAliveInterval. // // TODO(ameshkov): Consider making it configurable. QUICKeepAlivePeriod = time.Second * 20 @@ -157,7 +157,7 @@ func (doq *dnsOverQUIC) Close() (err error) { // through it and return the response it got from the server. func (doq *dnsOverQUIC) exchangeQUIC(ctx context.Context, msg *D.Msg) (resp *D.Msg, err error) { var conn quic.Connection - conn, err = doq.getConnection(ctx,true) + conn, err = doq.getConnection(ctx, true) if err != nil { return nil, err } @@ -225,7 +225,7 @@ func (doq *dnsOverQUIC) getBytesPool() (pool *sync.Pool) { // argument controls whether we should try to use the existing cached // connection. If it is false, we will forcibly create a new connection and // close the existing one if needed. -func (doq *dnsOverQUIC) getConnection(ctx context.Context,useCached bool) (quic.Connection, error) { +func (doq *dnsOverQUIC) getConnection(ctx context.Context, useCached bool) (quic.Connection, error) { var conn quic.Connection doq.connMu.RLock() conn = doq.conn @@ -292,7 +292,7 @@ func (doq *dnsOverQUIC) openStream(ctx context.Context, conn quic.Connection) (q // We can get here if the old QUIC connection is not valid anymore. We // should try to re-create the connection again in this case. - newConn, err := doq.getConnection(ctx,false) + newConn, err := doq.getConnection(ctx, false) if err != nil { return nil, err } @@ -455,7 +455,7 @@ func isQUICRetryError(err error) (ok bool) { // and that's why one can run into this. // In addition to that, quic-go HTTP3 client implementation does not // clean up dead connections (this one is specific to DoH3 upstream): - // https://github.com/lucas-clemente/quic-go/issues/765 + // https://github.com/metacubex/quic-go/issues/765 return true } diff --git a/docs/config.yaml b/docs/config.yaml index 39ed60be..f2d75bb5 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -468,8 +468,9 @@ proxies: # alpn: [h3] # disable_sni: true reduce_rtt: true -# request_timeout: 8000 - udp_relay_mode: native + # request_timeout: 8000 + udp_relay_mode: native # Available: "native", "quic". Default: "native" + # congestion_controller: bbr # Available: "cubic", "new_reno", "bbr". Default: "cubic" # skip-cert-verify: true # ShadowsocksR diff --git a/go.mod b/go.mod index 6a06a917..06fa6eb8 100644 --- a/go.mod +++ b/go.mod @@ -17,9 +17,9 @@ require ( github.com/hashicorp/golang-lru v0.5.4 github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c github.com/jpillora/backoff v1.0.0 - github.com/lucas-clemente/quic-go v0.29.1 github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/mdlayher/netlink v1.1.1 + github.com/metacubex/quic-go v0.31.1-0.20221125020617-0f0618ad3eaa github.com/metacubex/sing-wireguard v0.0.0-20221109114053-16c22adda03c github.com/miekg/dns v1.1.50 github.com/oschwald/geoip2-golang v1.8.0 @@ -43,11 +43,8 @@ require ( google.golang.org/protobuf v1.28.1 gopkg.in/yaml.v3 v3.0.1 lukechampine.com/blake3 v1.1.7 - ) -replace github.com/lucas-clemente/quic-go => github.com/HyNetwork/quic-go v0.30.1-0.20221105180419-83715d7269a8 - replace github.com/sagernet/sing-tun => github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc replace github.com/sagernet/sing-shadowsocks => github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221116103607-48a7095182b1 diff --git a/go.sum b/go.sum index fe36da98..73287249 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/HyNetwork/quic-go v0.30.1-0.20221105180419-83715d7269a8 h1:FBo40lMrk1bZZzJRJx8U+bQUPhLDGTUJ/Q5NV5BbO4Q= -github.com/HyNetwork/quic-go v0.30.1-0.20221105180419-83715d7269a8/go.mod h1:ssOrRsOmdxa768Wr78vnh2B8JozgLsMzG/g+0qEC7uk= github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221116103607-48a7095182b1 h1:guMcwJSIjk+jg/38uMPK5hIWVSaLHJ/l+ABZ8w2CKm0= github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221116103607-48a7095182b1/go.mod h1:3bW+hVWFXOxRC1HL6CO6QHkegqjLohErGbcvt6dUN18= github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc h1:B1vmR4cwDjJQ6YM8NkuXmt9vIYOdV/+qA+F3UAFE3YY= @@ -91,6 +89,8 @@ github.com/mdlayher/netlink v1.1.1 h1:VqG+Voq9V4uZ+04vjIrcSCWDpf91B1xxbP4QBUmUJE github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= +github.com/metacubex/quic-go v0.31.1-0.20221125020617-0f0618ad3eaa h1:Zc0nF/kgKDRn/Ab9jL1KNjOOe8JaM/sjsak1gNVVG6g= +github.com/metacubex/quic-go v0.31.1-0.20221125020617-0f0618ad3eaa/go.mod h1:7NPWVTLiX2Ss9q9gBNZaNHsPqZ3Tg/ApyrXxxUYbl78= github.com/metacubex/sing-wireguard v0.0.0-20221109114053-16c22adda03c h1:VHtXDny/TNOF7YDT9d9Qkr+x6K1O4cejXLlyPUXDeXQ= github.com/metacubex/sing-wireguard v0.0.0-20221109114053-16c22adda03c/go.mod h1:fULJ451x1/XlpIhl+Oo+EPGKla9tFZaqT5dKLrZ+NvM= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= diff --git a/test/go.mod b/test/go.mod index cc9432da..f048334d 100644 --- a/test/go.mod +++ b/test/go.mod @@ -8,21 +8,17 @@ require ( github.com/docker/go-connections v0.4.0 github.com/miekg/dns v1.1.50 github.com/stretchr/testify v1.8.1 - golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0 + golang.org/x/net v0.2.0 ) replace github.com/Dreamacro/clash => ../ -replace github.com/vishvananda/netlink => github.com/MetaCubeX/netlink v1.2.0-beta.0.20220529072258-d6853f887820 - -replace github.com/lucas-clemente/quic-go => github.com/tobyxdd/quic-go v0.27.1-0.20220512040129-ed2a645d9218 - require ( github.com/Microsoft/go-winio v0.5.1 // indirect - github.com/cheekybits/genny v1.0.0 // indirect + github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect github.com/cilium/ebpf v0.9.3 // indirect github.com/coreos/go-iptables v0.6.0 // indirect - github.com/database64128/tfo-go v1.1.2 // indirect + github.com/database64128/tfo-go/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dlclark/regexp2 v1.7.0 // indirect github.com/docker/distribution v2.8.1+incompatible // indirect @@ -31,23 +27,24 @@ require ( github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/gofrs/uuid v4.3.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/mock v1.6.0 // indirect github.com/google/btree v1.0.1 // indirect github.com/google/gopacket v1.1.19 // indirect + github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c // indirect + github.com/jpillora/backoff v1.0.0 // indirect github.com/klauspost/cpuid/v2 v2.0.12 // indirect - github.com/lucas-clemente/quic-go v0.29.1 // indirect github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect github.com/marten-seemann/qpack v0.3.0 // indirect - github.com/marten-seemann/qtls-go1-16 v0.1.5 // indirect - github.com/marten-seemann/qtls-go1-17 v0.1.1 // indirect github.com/marten-seemann/qtls-go1-18 v0.1.3 // indirect + github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect github.com/mdlayher/netlink v1.1.1 // indirect + github.com/metacubex/quic-go v0.31.1-0.20221125020617-0f0618ad3eaa // indirect github.com/metacubex/sing-wireguard v0.0.0-20221109114053-16c22adda03c // indirect github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c // indirect github.com/morikuni/aec v1.0.0 // indirect - github.com/nxadm/tail v1.4.8 // indirect - github.com/onsi/ginkgo v1.16.4 // indirect + github.com/onsi/ginkgo/v2 v2.2.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect github.com/oschwald/geoip2-golang v1.8.0 // indirect @@ -58,8 +55,8 @@ require ( 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.0.0-20221008120626-60a9910eefe4 // indirect - github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 // indirect - github.com/sagernet/sing-tun v0.0.0-20221012082254-488c3b75f6fd // indirect + github.com/sagernet/sing-shadowsocks v0.0.0-20221112030934-e55284e180ea // indirect + github.com/sagernet/sing-tun v0.0.0-20221104121441-66c48a57776f // indirect github.com/sagernet/sing-vmess v0.0.0-20221109021549-b446d5bdddf0 // indirect github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c // indirect github.com/sirupsen/logrus v1.9.0 // indirect @@ -68,16 +65,15 @@ require ( github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837 // indirect go.etcd.io/bbolt v1.3.6 // indirect go.uber.org/atomic v1.10.0 // indirect - golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077 // indirect + golang.org/x/crypto v0.2.0 // indirect golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06 // indirect + golang.org/x/sys v0.2.0 // indirect golang.org/x/text v0.4.0 // indirect golang.org/x/time v0.0.0-20191024005414-555d28b269f0 // indirect golang.org/x/tools v0.1.12 // indirect google.golang.org/protobuf v1.28.1 // indirect - gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools/v3 v3.4.0 // indirect gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c // indirect diff --git a/test/go.sum b/test/go.sum index 4a057a02..6dbd3b38 100644 --- a/test/go.sum +++ b/test/go.sum @@ -1,30 +1,17 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= -dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= -dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= -dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= -dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= -git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.5.1 h1:aPJp2QD7OOrhO5tQXqQoGSJc+DjDtWTGLOmNyAm6FgY= github.com/Microsoft/go-winio v0.5.1/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= -github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= -github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE= -github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= +github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= +github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= +github.com/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.9.3 h1:5KtxXZU+scyERvkJMEm16TbScVvuuMrlhPly78ZMbSc= github.com/cilium/ebpf v0.9.3/go.mod h1:w27N4UjpaQ9X/DGrSugxUG+H+NhgntDuPb5lCzxCn8A= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk= github.com/coreos/go-iptables v0.6.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q= -github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/database64128/tfo-go v1.1.2 h1:GwxtJp09BdUTVEoeT421t231eNZoGOCRkklbl4WI1kU= -github.com/database64128/tfo-go v1.1.2/go.mod h1:jgrSUPyOvTGQyn6irCOpk7L2W/q/0VLZZcovQiMi+bI= +github.com/database64128/tfo-go/v2 v2.0.2 h1:5rGgkJeLEKlNaqredfrPQNLnctn1b+1fq/8tdKdOzJg= +github.com/database64128/tfo-go/v2 v2.0.2/go.mod h1:FDdt4JaAsRU66wsYHxSVytYimPkKIHupVsxM+5DhvjY= 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= @@ -38,44 +25,20 @@ github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKoh github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= -github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= -github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gofrs/uuid v4.3.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc= github.com/gofrs/uuid v4.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -85,58 +48,40 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= -github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c h1:OCFM4+DXTWfNlyeoddrTwdup/ztkGSyAMR2UGcPckNQ= github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c/go.mod h1:h+MxyHxRg9NH3terB1nfRIUaQEcI0XOVkdR9LNBlp8E= -github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw= github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ= github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok= github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c h1:7cpGGTQO6+OuYQWkueqeXuErSjs1NZtpALpv1x7Mq4g= github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg= -github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc= github.com/marten-seemann/qpack v0.3.0 h1:UiWstOgT8+znlkDPOg2+3rIuYXJ2CnGDkGUXN6ki6hE= github.com/marten-seemann/qpack v0.3.0/go.mod h1:cGfKPBiP4a9EQdxCwEwI/GEeWAsjSekBvx/X8mh58+g= -github.com/marten-seemann/qtls-go1-16 v0.1.5 h1:o9JrYPPco/Nukd/HpOHMHZoBDXQqoNtUCmny98/1uqQ= -github.com/marten-seemann/qtls-go1-16 v0.1.5/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk= -github.com/marten-seemann/qtls-go1-17 v0.1.1 h1:DQjHPq+aOzUeh9/lixAGunn6rIOQyWChPSI4+hgW7jc= -github.com/marten-seemann/qtls-go1-17 v0.1.1/go.mod h1:C2ekUKcDdz9SDWxec1N/MvcXBpaX9l3Nx67XaR84L5s= -github.com/marten-seemann/qtls-go1-18 v0.1.1/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= github.com/marten-seemann/qtls-go1-18 v0.1.3 h1:R4H2Ks8P6pAtUagjFty2p7BVHn3XiwDAl7TTQf5h7TI= github.com/marten-seemann/qtls-go1-18 v0.1.3/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/marten-seemann/qtls-go1-19 v0.1.1 h1:mnbxeq3oEyQxQXwI4ReCgW9DPoPR94sNlqWoDZnjRIE= +github.com/marten-seemann/qtls-go1-19 v0.1.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y= github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= @@ -145,53 +90,32 @@ github.com/mdlayher/netlink v1.1.1 h1:VqG+Voq9V4uZ+04vjIrcSCWDpf91B1xxbP4QBUmUJE github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= +github.com/metacubex/quic-go v0.31.1-0.20221125020617-0f0618ad3eaa h1:Zc0nF/kgKDRn/Ab9jL1KNjOOe8JaM/sjsak1gNVVG6g= +github.com/metacubex/quic-go v0.31.1-0.20221125020617-0f0618ad3eaa/go.mod h1:7NPWVTLiX2Ss9q9gBNZaNHsPqZ3Tg/ApyrXxxUYbl78= github.com/metacubex/sing-wireguard v0.0.0-20221109114053-16c22adda03c h1:VHtXDny/TNOF7YDT9d9Qkr+x6K1O4cejXLlyPUXDeXQ= github.com/metacubex/sing-wireguard v0.0.0-20221109114053-16c22adda03c/go.mod h1:fULJ451x1/XlpIhl+Oo+EPGKla9tFZaqT5dKLrZ+NvM= -github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c h1:RC8WMpjonrBfyAh6VN/POIPtYD5tRAq0qMqCRjQNK+g= github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c/go.mod h1:9OcmHNQQUTbk4XCffrLgN1NEKc2mh5u++biHVrvHsSU= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= -github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= -github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= -github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= +github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs= github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw= github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg= github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e h1:5CFRo8FJbCuf5s/eTBdZpmMbn8Fe2eSMLNAYfKanA34= github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e/go.mod h1:qbt0dWObotCfcjAJJ9AxtFPNSDUfZF+6dCpgKEOBn/g= github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA= @@ -201,44 +125,19 @@ github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJ github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4 h1:LO7xMvMGhYmjQg2vjhTzsODyzs9/WLYu5Per+/8jIeo= github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4= -github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6 h1:JJfDeYYhWunvtxsU/mOVNTmFQmnzGx9dY034qG6G3g4= -github.com/sagernet/sing-shadowsocks v0.0.0-20220819002358-7461bb09a8f6/go.mod h1:EX3RbZvrwAkPI2nuGa78T2iQXmrkT+/VQtskjou42xM= -github.com/sagernet/sing-tun v0.0.0-20221012082254-488c3b75f6fd h1:TtoZDwg09Cpqi+gCmCtL6w4oEUZ5lHz+vHIjdr1UBNY= -github.com/sagernet/sing-tun v0.0.0-20221012082254-488c3b75f6fd/go.mod h1:1u3pjXA9HmH7kRiBJqM3C/zPxrxnCLd3svmqtub/RFU= +github.com/sagernet/sing-shadowsocks v0.0.0-20221112030934-e55284e180ea h1:OO+oV7XNQ7eah2bQyT2pSAoeCBHpbAU3cVVbGd7TE/g= +github.com/sagernet/sing-shadowsocks v0.0.0-20221112030934-e55284e180ea/go.mod h1:16sNARQbsFbYIzAuPySszQA6Wfgzk7GWSzh1a6kDrUU= +github.com/sagernet/sing-tun v0.0.0-20221104121441-66c48a57776f h1:CXF+nErOb9f7qiHingSgTa2/lJAgmEFtAQ47oVwdRGU= +github.com/sagernet/sing-tun v0.0.0-20221104121441-66c48a57776f/go.mod h1:1u3pjXA9HmH7kRiBJqM3C/zPxrxnCLd3svmqtub/RFU= github.com/sagernet/sing-vmess v0.0.0-20221109021549-b446d5bdddf0 h1:z3kuD3hPNdEq7/wVy5lwE21f+8ZTazBtR81qswxJoCc= github.com/sagernet/sing-vmess v0.0.0-20221109021549-b446d5bdddf0/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM= github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c h1:qP3ZOHnjZalvqbjundbXiv/YrNlo3HOgrKc+S1QGs0U= github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= -github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= -github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= -github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= -github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= -github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= -github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= -github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= -github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= -github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= -github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= -github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= -github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= -github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= -github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= -github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= -github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= -github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= -github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= -github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= -github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -250,13 +149,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= -github.com/tobyxdd/quic-go v0.27.1-0.20220512040129-ed2a645d9218 h1:0DEghzcIfYe+7HTuI+zEd/5M+5c/gcepjJWGdcPPIrc= -github.com/tobyxdd/quic-go v0.27.1-0.20220512040129-ed2a645d9218/go.mod h1:AzgQoPda7N+3IqMMMkywBKggIFo2KT6pfnlrQ2QieeI= github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA= github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= -github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= -github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg= github.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= github.com/xtls/go v0.0.0-20220914232946-0441cf4cf837 h1:AHhUwwFJGl27E46OpdJHplZkK09m7aETNBNzhT6t15M= @@ -266,25 +160,15 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= -go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= -golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= -golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/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-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077 h1:t5bjOfJPQfaG9NV1imLZM5E2uzaLGs5/NtyMtRNVjQ4= -golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/crypto v0.2.0 h1:BRXPfhNivWL5Yq0BGQ39a2sW6t44aODpfxkWjYdzewE= +golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9 h1:RjggHMcaTVp0LOVZcW0bo8alwHrOaCrGUDgfWUHhnN4= golang.org/x/exp v0.0.0-20220930202632-ec3f01382ef9/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 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= @@ -292,15 +176,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= @@ -309,94 +185,61 @@ golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0 h1:vZ44Ys50wUISbPd+jC8cRLSvhyfX9Ii/ZmDnn/aiJtM= -golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= 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.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06 h1:E1pm64FqQa4v8dHd/bAneyMkR4hk8LTJhoSlc5mc1cM= -golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -407,56 +250,18 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T 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= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= -google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= -google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= -grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c h1:m5lcgWnL3OElQNVyp3qcncItJ2c0sQlSGjYK2+nJTA4= gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= -sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= -sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/transport/hysteria/congestion/brutal.go b/transport/hysteria/congestion/brutal.go index d08ac346..8f02ef14 100644 --- a/transport/hysteria/congestion/brutal.go +++ b/transport/hysteria/congestion/brutal.go @@ -1,7 +1,7 @@ package congestion import ( - "github.com/lucas-clemente/quic-go/congestion" + "github.com/metacubex/quic-go/congestion" "time" ) diff --git a/transport/hysteria/congestion/pacer.go b/transport/hysteria/congestion/pacer.go index 43707108..2dff5300 100644 --- a/transport/hysteria/congestion/pacer.go +++ b/transport/hysteria/congestion/pacer.go @@ -1,7 +1,7 @@ package congestion import ( - "github.com/lucas-clemente/quic-go/congestion" + "github.com/metacubex/quic-go/congestion" "math" "time" ) diff --git a/transport/hysteria/core/client.go b/transport/hysteria/core/client.go index bd91250d..9c93f5a5 100644 --- a/transport/hysteria/core/client.go +++ b/transport/hysteria/core/client.go @@ -10,9 +10,9 @@ import ( "github.com/Dreamacro/clash/transport/hysteria/pmtud_fix" "github.com/Dreamacro/clash/transport/hysteria/transport" "github.com/Dreamacro/clash/transport/hysteria/utils" - "github.com/lucas-clemente/quic-go" - "github.com/lucas-clemente/quic-go/congestion" "github.com/lunixbochs/struc" + "github.com/metacubex/quic-go" + "github.com/metacubex/quic-go/congestion" "math/rand" "net" "strconv" diff --git a/transport/hysteria/core/stream.go b/transport/hysteria/core/stream.go index 8ace4a1d..627b4789 100644 --- a/transport/hysteria/core/stream.go +++ b/transport/hysteria/core/stream.go @@ -2,7 +2,7 @@ package core import ( "context" - "github.com/lucas-clemente/quic-go" + "github.com/metacubex/quic-go" "time" ) diff --git a/transport/hysteria/transport/client.go b/transport/hysteria/transport/client.go index 43330cd3..a48e9bf5 100644 --- a/transport/hysteria/transport/client.go +++ b/transport/hysteria/transport/client.go @@ -8,7 +8,7 @@ import ( "github.com/Dreamacro/clash/transport/hysteria/conns/udp" "github.com/Dreamacro/clash/transport/hysteria/conns/wechat" obfsPkg "github.com/Dreamacro/clash/transport/hysteria/obfs" - "github.com/lucas-clemente/quic-go" + "github.com/metacubex/quic-go" "net" ) diff --git a/transport/tuic/client.go b/transport/tuic/client.go index 3d9506c1..82ff4c9a 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -12,20 +12,22 @@ import ( "sync" "time" - "github.com/lucas-clemente/quic-go" + "github.com/metacubex/quic-go" N "github.com/Dreamacro/clash/common/net" C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/transport/tuic/congestion" ) type Client struct { - TlsConfig *tls.Config - QuicConfig *quic.Config - Host string - Token [32]byte - UdpRelayMode string - ReduceRtt bool - RequestTimeout int + TlsConfig *tls.Config + QuicConfig *quic.Config + Host string + Token [32]byte + UdpRelayMode string + CongestionController string + ReduceRtt bool + RequestTimeout int quicConn quic.Connection connMutex sync.Mutex @@ -53,6 +55,36 @@ func (t *Client) getQuicConn(ctx context.Context, dialFn func(ctx context.Contex return nil, err } + switch t.CongestionController { + case "cubic": + quicConn.SetCongestionControl( + congestion.NewCubicSender( + congestion.DefaultClock{}, + congestion.GetMaxPacketSize(quicConn.RemoteAddr()), + false, + nil, + ), + ) + case "new_reno": + quicConn.SetCongestionControl( + congestion.NewCubicSender( + congestion.DefaultClock{}, + congestion.GetMaxPacketSize(quicConn.RemoteAddr()), + true, + nil, + ), + ) + case "bbr": + quicConn.SetCongestionControl( + congestion.NewBBRSender( + congestion.DefaultClock{}, + congestion.GetMaxPacketSize(quicConn.RemoteAddr()), + congestion.InitialCongestionWindow, + congestion.DefaultBBRMaxCongestionWindow, + ), + ) + } + sendAuthentication := func(quicConn quic.Connection) (err error) { defer func() { t.deferQuicConn(quicConn, err) diff --git a/transport/tuic/congestion/bandwidth.go b/transport/tuic/congestion/bandwidth.go new file mode 100644 index 00000000..2a6b3a2e --- /dev/null +++ b/transport/tuic/congestion/bandwidth.go @@ -0,0 +1,25 @@ +package congestion + +import ( + "math" + "time" + + "github.com/metacubex/quic-go/congestion" +) + +// Bandwidth of a connection +type Bandwidth uint64 + +const infBandwidth Bandwidth = math.MaxUint64 + +const ( + // BitsPerSecond is 1 bit per second + BitsPerSecond Bandwidth = 1 + // BytesPerSecond is 1 byte per second + BytesPerSecond = 8 * BitsPerSecond +) + +// BandwidthFromDelta calculates the bandwidth from a number of bytes and a time delta +func BandwidthFromDelta(bytes congestion.ByteCount, delta time.Duration) Bandwidth { + return Bandwidth(bytes) * Bandwidth(time.Second) / Bandwidth(delta) * BytesPerSecond +} diff --git a/transport/tuic/congestion/bandwidth_sampler.go b/transport/tuic/congestion/bandwidth_sampler.go new file mode 100644 index 00000000..b82d391f --- /dev/null +++ b/transport/tuic/congestion/bandwidth_sampler.go @@ -0,0 +1,376 @@ +package congestion + +import ( + "math" + "time" + + "github.com/metacubex/quic-go/congestion" +) + +var ( + InfiniteBandwidth = Bandwidth(math.MaxUint64) +) + +// SendTimeState is a subset of ConnectionStateOnSentPacket which is returned +// to the caller when the packet is acked or lost. +type SendTimeState struct { + // Whether other states in this object is valid. + isValid bool + // Whether the sender is app limited at the time the packet was sent. + // App limited bandwidth sample might be artificially low because the sender + // did not have enough data to send in order to saturate the link. + isAppLimited bool + // Total number of sent bytes at the time the packet was sent. + // Includes the packet itself. + totalBytesSent congestion.ByteCount + // Total number of acked bytes at the time the packet was sent. + totalBytesAcked congestion.ByteCount + // Total number of lost bytes at the time the packet was sent. + totalBytesLost congestion.ByteCount +} + +// ConnectionStateOnSentPacket represents the information about a sent packet +// and the state of the connection at the moment the packet was sent, +// specifically the information about the most recently acknowledged packet at +// that moment. +type ConnectionStateOnSentPacket struct { + packetNumber congestion.PacketNumber + // Time at which the packet is sent. + sendTime time.Time + // Size of the packet. + size congestion.ByteCount + // The value of |totalBytesSentAtLastAckedPacket| at the time the + // packet was sent. + totalBytesSentAtLastAckedPacket congestion.ByteCount + // The value of |lastAckedPacketSentTime| at the time the packet was + // sent. + lastAckedPacketSentTime time.Time + // The value of |lastAckedPacketAckTime| at the time the packet was + // sent. + lastAckedPacketAckTime time.Time + // Send time states that are returned to the congestion controller when the + // packet is acked or lost. + sendTimeState SendTimeState +} + +// BandwidthSample +type BandwidthSample struct { + // The bandwidth at that particular sample. Zero if no valid bandwidth sample + // is available. + bandwidth Bandwidth + // The RTT measurement at this particular sample. Zero if no RTT sample is + // available. Does not correct for delayed ack time. + rtt time.Duration + // States captured when the packet was sent. + stateAtSend SendTimeState +} + +func NewBandwidthSample() *BandwidthSample { + return &BandwidthSample{ + // FIXME: the default value of original code is zero. + rtt: InfiniteRTT, + } +} + +// BandwidthSampler keeps track of sent and acknowledged packets and outputs a +// bandwidth sample for every packet acknowledged. The samples are taken for +// individual packets, and are not filtered; the consumer has to filter the +// bandwidth samples itself. In certain cases, the sampler will locally severely +// underestimate the bandwidth, hence a maximum filter with a size of at least +// one RTT is recommended. +// +// This class bases its samples on the slope of two curves: the number of bytes +// sent over time, and the number of bytes acknowledged as received over time. +// It produces a sample of both slopes for every packet that gets acknowledged, +// based on a slope between two points on each of the corresponding curves. Note +// that due to the packet loss, the number of bytes on each curve might get +// further and further away from each other, meaning that it is not feasible to +// compare byte values coming from different curves with each other. +// +// The obvious points for measuring slope sample are the ones corresponding to +// the packet that was just acknowledged. Let us denote them as S_1 (point at +// which the current packet was sent) and A_1 (point at which the current packet +// was acknowledged). However, taking a slope requires two points on each line, +// so estimating bandwidth requires picking a packet in the past with respect to +// which the slope is measured. +// +// For that purpose, BandwidthSampler always keeps track of the most recently +// acknowledged packet, and records it together with every outgoing packet. +// When a packet gets acknowledged (A_1), it has not only information about when +// it itself was sent (S_1), but also the information about the latest +// acknowledged packet right before it was sent (S_0 and A_0). +// +// Based on that data, send and ack rate are estimated as: +// +// send_rate = (bytes(S_1) - bytes(S_0)) / (time(S_1) - time(S_0)) +// ack_rate = (bytes(A_1) - bytes(A_0)) / (time(A_1) - time(A_0)) +// +// Here, the ack rate is intuitively the rate we want to treat as bandwidth. +// However, in certain cases (e.g. ack compression) the ack rate at a point may +// end up higher than the rate at which the data was originally sent, which is +// not indicative of the real bandwidth. Hence, we use the send rate as an upper +// bound, and the sample value is +// +// rate_sample = min(send_rate, ack_rate) +// +// An important edge case handled by the sampler is tracking the app-limited +// samples. There are multiple meaning of "app-limited" used interchangeably, +// hence it is important to understand and to be able to distinguish between +// them. +// +// Meaning 1: connection state. The connection is said to be app-limited when +// there is no outstanding data to send. This means that certain bandwidth +// samples in the future would not be an accurate indication of the link +// capacity, and it is important to inform consumer about that. Whenever +// connection becomes app-limited, the sampler is notified via OnAppLimited() +// method. +// +// Meaning 2: a phase in the bandwidth sampler. As soon as the bandwidth +// sampler becomes notified about the connection being app-limited, it enters +// app-limited phase. In that phase, all *sent* packets are marked as +// app-limited. Note that the connection itself does not have to be +// app-limited during the app-limited phase, and in fact it will not be +// (otherwise how would it send packets?). The boolean flag below indicates +// whether the sampler is in that phase. +// +// Meaning 3: a flag on the sent packet and on the sample. If a sent packet is +// sent during the app-limited phase, the resulting sample related to the +// packet will be marked as app-limited. +// +// With the terminology issue out of the way, let us consider the question of +// what kind of situation it addresses. +// +// Consider a scenario where we first send packets 1 to 20 at a regular +// bandwidth, and then immediately run out of data. After a few seconds, we send +// packets 21 to 60, and only receive ack for 21 between sending packets 40 and +// 41. In this case, when we sample bandwidth for packets 21 to 40, the S_0/A_0 +// we use to compute the slope is going to be packet 20, a few seconds apart +// from the current packet, hence the resulting estimate would be extremely low +// and not indicative of anything. Only at packet 41 the S_0/A_0 will become 21, +// meaning that the bandwidth sample would exclude the quiescence. +// +// Based on the analysis of that scenario, we implement the following rule: once +// OnAppLimited() is called, all sent packets will produce app-limited samples +// up until an ack for a packet that was sent after OnAppLimited() was called. +// Note that while the scenario above is not the only scenario when the +// connection is app-limited, the approach works in other cases too. +type BandwidthSampler struct { + // The total number of congestion controlled bytes sent during the connection. + totalBytesSent congestion.ByteCount + // The total number of congestion controlled bytes which were acknowledged. + totalBytesAcked congestion.ByteCount + // The total number of congestion controlled bytes which were lost. + totalBytesLost congestion.ByteCount + // The value of |totalBytesSent| at the time the last acknowledged packet + // was sent. Valid only when |lastAckedPacketSentTime| is valid. + totalBytesSentAtLastAckedPacket congestion.ByteCount + // The time at which the last acknowledged packet was sent. Set to + // QuicTime::Zero() if no valid timestamp is available. + lastAckedPacketSentTime time.Time + // The time at which the most recent packet was acknowledged. + lastAckedPacketAckTime time.Time + // The most recently sent packet. + lastSendPacket congestion.PacketNumber + // Indicates whether the bandwidth sampler is currently in an app-limited + // phase. + isAppLimited bool + // The packet that will be acknowledged after this one will cause the sampler + // to exit the app-limited phase. + endOfAppLimitedPhase congestion.PacketNumber + // Record of the connection state at the point where each packet in flight was + // sent, indexed by the packet number. + connectionStats *ConnectionStates +} + +func NewBandwidthSampler() *BandwidthSampler { + return &BandwidthSampler{ + connectionStats: &ConnectionStates{ + stats: make(map[congestion.PacketNumber]*ConnectionStateOnSentPacket), + }, + } +} + +// OnPacketSent Inputs the sent packet information into the sampler. Assumes that all +// packets are sent in order. The information about the packet will not be +// released from the sampler until it the packet is either acknowledged or +// declared lost. +func (s *BandwidthSampler) OnPacketSent(sentTime time.Time, lastSentPacket congestion.PacketNumber, sentBytes, bytesInFlight congestion.ByteCount, hasRetransmittableData bool) { + s.lastSendPacket = lastSentPacket + + if !hasRetransmittableData { + return + } + + s.totalBytesSent += sentBytes + + // If there are no packets in flight, the time at which the new transmission + // opens can be treated as the A_0 point for the purpose of bandwidth + // sampling. This underestimates bandwidth to some extent, and produces some + // artificially low samples for most packets in flight, but it provides with + // samples at important points where we would not have them otherwise, most + // importantly at the beginning of the connection. + if bytesInFlight == 0 { + s.lastAckedPacketAckTime = sentTime + s.totalBytesSentAtLastAckedPacket = s.totalBytesSent + + // In this situation ack compression is not a concern, set send rate to + // effectively infinite. + s.lastAckedPacketSentTime = sentTime + } + + s.connectionStats.Insert(lastSentPacket, sentTime, sentBytes, s) +} + +// OnPacketAcked Notifies the sampler that the |lastAckedPacket| is acknowledged. Returns a +// bandwidth sample. If no bandwidth sample is available, +// QuicBandwidth::Zero() is returned. +func (s *BandwidthSampler) OnPacketAcked(ackTime time.Time, lastAckedPacket congestion.PacketNumber) *BandwidthSample { + sentPacketState := s.connectionStats.Get(lastAckedPacket) + if sentPacketState == nil { + return NewBandwidthSample() + } + + sample := s.onPacketAckedInner(ackTime, lastAckedPacket, sentPacketState) + s.connectionStats.Remove(lastAckedPacket) + + return sample +} + +// onPacketAckedInner Handles the actual bandwidth calculations, whereas the outer method handles +// retrieving and removing |sentPacket|. +func (s *BandwidthSampler) onPacketAckedInner(ackTime time.Time, lastAckedPacket congestion.PacketNumber, sentPacket *ConnectionStateOnSentPacket) *BandwidthSample { + s.totalBytesAcked += sentPacket.size + + s.totalBytesSentAtLastAckedPacket = sentPacket.sendTimeState.totalBytesSent + s.lastAckedPacketSentTime = sentPacket.sendTime + s.lastAckedPacketAckTime = ackTime + + // Exit app-limited phase once a packet that was sent while the connection is + // not app-limited is acknowledged. + if s.isAppLimited && lastAckedPacket > s.endOfAppLimitedPhase { + s.isAppLimited = false + } + + // There might have been no packets acknowledged at the moment when the + // current packet was sent. In that case, there is no bandwidth sample to + // make. + if sentPacket.lastAckedPacketSentTime.IsZero() { + return NewBandwidthSample() + } + + // Infinite rate indicates that the sampler is supposed to discard the + // current send rate sample and use only the ack rate. + sendRate := InfiniteBandwidth + if sentPacket.sendTime.After(sentPacket.lastAckedPacketSentTime) { + sendRate = BandwidthFromDelta(sentPacket.sendTimeState.totalBytesSent-sentPacket.totalBytesSentAtLastAckedPacket, sentPacket.sendTime.Sub(sentPacket.lastAckedPacketSentTime)) + } + + // During the slope calculation, ensure that ack time of the current packet is + // always larger than the time of the previous packet, otherwise division by + // zero or integer underflow can occur. + if !ackTime.After(sentPacket.lastAckedPacketAckTime) { + // TODO(wub): Compare this code count before and after fixing clock jitter + // issue. + // if sentPacket.lastAckedPacketAckTime.Equal(sentPacket.sendTime) { + // This is the 1st packet after quiescense. + // QUIC_CODE_COUNT_N(quic_prev_ack_time_larger_than_current_ack_time, 1, 2); + // } else { + // QUIC_CODE_COUNT_N(quic_prev_ack_time_larger_than_current_ack_time, 2, 2); + // } + + return NewBandwidthSample() + } + + ackRate := BandwidthFromDelta(s.totalBytesAcked-sentPacket.sendTimeState.totalBytesAcked, + ackTime.Sub(sentPacket.lastAckedPacketAckTime)) + + // Note: this sample does not account for delayed acknowledgement time. This + // means that the RTT measurements here can be artificially high, especially + // on low bandwidth connections. + sample := &BandwidthSample{ + bandwidth: minBandwidth(sendRate, ackRate), + rtt: ackTime.Sub(sentPacket.sendTime), + } + + SentPacketToSendTimeState(sentPacket, &sample.stateAtSend) + return sample +} + +// OnPacketLost Informs the sampler that a packet is considered lost and it should no +// longer keep track of it. +func (s *BandwidthSampler) OnPacketLost(packetNumber congestion.PacketNumber) SendTimeState { + ok, sentPacket := s.connectionStats.Remove(packetNumber) + sendTimeState := SendTimeState{ + isValid: ok, + } + if sentPacket != nil { + s.totalBytesLost += sentPacket.size + SentPacketToSendTimeState(sentPacket, &sendTimeState) + } + + return sendTimeState +} + +// OnAppLimited Informs the sampler that the connection is currently app-limited, causing +// the sampler to enter the app-limited phase. The phase will expire by +// itself. +func (s *BandwidthSampler) OnAppLimited() { + s.isAppLimited = true + s.endOfAppLimitedPhase = s.lastSendPacket +} + +// SentPacketToSendTimeState Copy a subset of the (private) ConnectionStateOnSentPacket to the (public) +// SendTimeState. Always set send_time_state->is_valid to true. +func SentPacketToSendTimeState(sentPacket *ConnectionStateOnSentPacket, sendTimeState *SendTimeState) { + sendTimeState.isAppLimited = sentPacket.sendTimeState.isAppLimited + sendTimeState.totalBytesSent = sentPacket.sendTimeState.totalBytesSent + sendTimeState.totalBytesAcked = sentPacket.sendTimeState.totalBytesAcked + sendTimeState.totalBytesLost = sentPacket.sendTimeState.totalBytesLost + sendTimeState.isValid = true +} + +// ConnectionStates Record of the connection state at the point where each packet in flight was +// sent, indexed by the packet number. +// FIXME: using LinkedList replace map to fast remove all the packets lower than the specified packet number. +type ConnectionStates struct { + stats map[congestion.PacketNumber]*ConnectionStateOnSentPacket +} + +func (s *ConnectionStates) Insert(packetNumber congestion.PacketNumber, sentTime time.Time, bytes congestion.ByteCount, sampler *BandwidthSampler) bool { + if _, ok := s.stats[packetNumber]; ok { + return false + } + + s.stats[packetNumber] = NewConnectionStateOnSentPacket(packetNumber, sentTime, bytes, sampler) + return true +} + +func (s *ConnectionStates) Get(packetNumber congestion.PacketNumber) *ConnectionStateOnSentPacket { + return s.stats[packetNumber] +} + +func (s *ConnectionStates) Remove(packetNumber congestion.PacketNumber) (bool, *ConnectionStateOnSentPacket) { + state, ok := s.stats[packetNumber] + if ok { + delete(s.stats, packetNumber) + } + return ok, state +} + +func NewConnectionStateOnSentPacket(packetNumber congestion.PacketNumber, sentTime time.Time, bytes congestion.ByteCount, sampler *BandwidthSampler) *ConnectionStateOnSentPacket { + return &ConnectionStateOnSentPacket{ + packetNumber: packetNumber, + sendTime: sentTime, + size: bytes, + lastAckedPacketSentTime: sampler.lastAckedPacketSentTime, + lastAckedPacketAckTime: sampler.lastAckedPacketAckTime, + totalBytesSentAtLastAckedPacket: sampler.totalBytesSentAtLastAckedPacket, + sendTimeState: SendTimeState{ + isValid: true, + isAppLimited: sampler.isAppLimited, + totalBytesSent: sampler.totalBytesSent, + totalBytesAcked: sampler.totalBytesAcked, + totalBytesLost: sampler.totalBytesLost, + }, + } +} diff --git a/transport/tuic/congestion/bbr_sender.go b/transport/tuic/congestion/bbr_sender.go new file mode 100644 index 00000000..d4ba20d1 --- /dev/null +++ b/transport/tuic/congestion/bbr_sender.go @@ -0,0 +1,992 @@ +package congestion + +// src from https://quiche.googlesource.com/quiche.git/+/66dea072431f94095dfc3dd2743cb94ef365f7ef/quic/core/congestion_control/bbr_sender.cc + +import ( + "fmt" + "math" + "math/rand" + "net" + "time" + + "github.com/metacubex/quic-go/congestion" +) + +const DefaultTCPMSS congestion.ByteCount = 1460 +const DefaultBBRMaxCongestionWindow congestion.ByteCount = 2000 * DefaultTCPMSS +const InitialCongestionWindow congestion.ByteCount = 32 * DefaultTCPMSS +const MinInitialPacketSize = 1200 +const InitialPacketSizeIPv4 = 1252 +const InitialPacketSizeIPv6 = 1232 + +func GetMaxPacketSize(addr net.Addr) congestion.ByteCount { + maxSize := congestion.ByteCount(MinInitialPacketSize) + // If this is not a UDP address, we don't know anything about the MTU. + // Use the minimum size of an Initial packet as the max packet size. + if udpAddr, ok := addr.(*net.UDPAddr); ok { + if udpAddr.IP.To4() != nil { + maxSize = InitialPacketSizeIPv4 + } else { + maxSize = InitialPacketSizeIPv6 + } + } + return maxSize +} + +var ( + // The maximum outgoing packet size allowed. + // The maximum packet size of any QUIC packet over IPv6, based on ethernet's max + // size, minus the IP and UDP headers. IPv6 has a 40 byte header, UDP adds an + // additional 8 bytes. This is a total overhead of 48 bytes. Ethernet's + // max packet size is 1500 bytes, 1500 - 48 = 1452. + MaxOutgoingPacketSize = congestion.ByteCount(1452) + + // Default maximum packet size used in the Linux TCP implementation. + // Used in QUIC for congestion window computations in bytes. + MaxSegmentSize = DefaultTCPMSS + + // Default initial rtt used before any samples are received. + InitialRtt = 100 * time.Millisecond + + // Constants based on TCP defaults. + // The minimum CWND to ensure delayed acks don't reduce bandwidth measurements. + // Does not inflate the pacing rate. + DefaultMinimumCongestionWindow = 4 * DefaultTCPMSS + + // The gain used for the STARTUP, equal to 2/ln(2). + DefaultHighGain = 2.885 + + // The gain used in STARTUP after loss has been detected. + // 1.5 is enough to allow for 25% exogenous loss and still observe a 25% growth + // in measured bandwidth. + StartupAfterLossGain = 1.5 + + // The cycle of gains used during the PROBE_BW stage. + PacingGain = []float64{1.25, 0.75, 1, 1, 1, 1, 1, 1} + + // The length of the gain cycle. + GainCycleLength = len(PacingGain) + + // The size of the bandwidth filter window, in round-trips. + BandwidthWindowSize = GainCycleLength + 2 + + // The time after which the current min_rtt value expires. + MinRttExpiry = 10 * time.Second + + // The minimum time the connection can spend in PROBE_RTT mode. + ProbeRttTime = time.Millisecond * 200 + + // If the bandwidth does not increase by the factor of |kStartupGrowthTarget| + // within |kRoundTripsWithoutGrowthBeforeExitingStartup| rounds, the connection + // will exit the STARTUP mode. + StartupGrowthTarget = 1.25 + RoundTripsWithoutGrowthBeforeExitingStartup = int64(3) + + // Coefficient of target congestion window to use when basing PROBE_RTT on BDP. + ModerateProbeRttMultiplier = 0.75 + + // Coefficient to determine if a new RTT is sufficiently similar to min_rtt that + // we don't need to enter PROBE_RTT. + SimilarMinRttThreshold = 1.125 + + // Congestion window gain for QUIC BBR during PROBE_BW phase. + DefaultCongestionWindowGainConst = 2.0 +) + +type bbrMode int + +const ( + // Startup phase of the connection. + STARTUP = iota + // After achieving the highest possible bandwidth during the startup, lower + // the pacing rate in order to drain the queue. + DRAIN + // Cruising mode. + PROBE_BW + // Temporarily slow down sending in order to empty the buffer and measure + // the real minimum RTT. + PROBE_RTT +) + +type bbrRecoveryState int + +const ( + // Do not limit. + NOT_IN_RECOVERY = iota + + // Allow an extra outstanding byte for each byte acknowledged. + CONSERVATION + + // Allow two extra outstanding bytes for each byte acknowledged (slow + // start). + GROWTH +) + +type bbrSender struct { + mode bbrMode + clock Clock + rttStats congestion.RTTStatsProvider + bytesInFlight congestion.ByteCount + // return total bytes of unacked packets. + //GetBytesInFlight func() congestion.ByteCount + // Bandwidth sampler provides BBR with the bandwidth measurements at + // individual points. + sampler *BandwidthSampler + // The number of the round trips that have occurred during the connection. + roundTripCount int64 + // The packet number of the most recently sent packet. + lastSendPacket congestion.PacketNumber + // Acknowledgement of any packet after |current_round_trip_end_| will cause + // the round trip counter to advance. + currentRoundTripEnd congestion.PacketNumber + // The filter that tracks the maximum bandwidth over the multiple recent + // round-trips. + maxBandwidth *WindowedFilter + // Tracks the maximum number of bytes acked faster than the sending rate. + maxAckHeight *WindowedFilter + // The time this aggregation started and the number of bytes acked during it. + aggregationEpochStartTime time.Time + aggregationEpochBytes congestion.ByteCount + // Minimum RTT estimate. Automatically expires within 10 seconds (and + // triggers PROBE_RTT mode) if no new value is sampled during that period. + minRtt time.Duration + // The time at which the current value of |min_rtt_| was assigned. + minRttTimestamp time.Time + // The maximum allowed number of bytes in flight. + congestionWindow congestion.ByteCount + // The initial value of the |congestion_window_|. + initialCongestionWindow congestion.ByteCount + // The largest value the |congestion_window_| can achieve. + maxCongestionWindow congestion.ByteCount + // The smallest value the |congestion_window_| can achieve. + minCongestionWindow congestion.ByteCount + // The pacing gain applied during the STARTUP phase. + highGain float64 + // The CWND gain applied during the STARTUP phase. + highCwndGain float64 + // The pacing gain applied during the DRAIN phase. + drainGain float64 + // The current pacing rate of the connection. + pacingRate Bandwidth + // The gain currently applied to the pacing rate. + pacingGain float64 + // The gain currently applied to the congestion window. + congestionWindowGain float64 + // The gain used for the congestion window during PROBE_BW. Latched from + // quic_bbr_cwnd_gain flag. + congestionWindowGainConst float64 + // The number of RTTs to stay in STARTUP mode. Defaults to 3. + numStartupRtts int64 + // If true, exit startup if 1RTT has passed with no bandwidth increase and + // the connection is in recovery. + exitStartupOnLoss bool + // Number of round-trips in PROBE_BW mode, used for determining the current + // pacing gain cycle. + cycleCurrentOffset int + // The time at which the last pacing gain cycle was started. + lastCycleStart time.Time + // Indicates whether the connection has reached the full bandwidth mode. + isAtFullBandwidth bool + // Number of rounds during which there was no significant bandwidth increase. + roundsWithoutBandwidthGain int64 + // The bandwidth compared to which the increase is measured. + bandwidthAtLastRound Bandwidth + // Set to true upon exiting quiescence. + exitingQuiescence bool + // Time at which PROBE_RTT has to be exited. Setting it to zero indicates + // that the time is yet unknown as the number of packets in flight has not + // reached the required value. + exitProbeRttAt time.Time + // Indicates whether a round-trip has passed since PROBE_RTT became active. + probeRttRoundPassed bool + // Indicates whether the most recent bandwidth sample was marked as + // app-limited. + lastSampleIsAppLimited bool + // Indicates whether any non app-limited samples have been recorded. + hasNoAppLimitedSample bool + // Indicates app-limited calls should be ignored as long as there's + // enough data inflight to see more bandwidth when necessary. + flexibleAppLimited bool + // Current state of recovery. + recoveryState bbrRecoveryState + // Receiving acknowledgement of a packet after |end_recovery_at_| will cause + // BBR to exit the recovery mode. A value above zero indicates at least one + // loss has been detected, so it must not be set back to zero. + endRecoveryAt congestion.PacketNumber + // A window used to limit the number of bytes in flight during loss recovery. + recoveryWindow congestion.ByteCount + // If true, consider all samples in recovery app-limited. + isAppLimitedRecovery bool + // When true, pace at 1.5x and disable packet conservation in STARTUP. + slowerStartup bool + // When true, disables packet conservation in STARTUP. + rateBasedStartup bool + // When non-zero, decreases the rate in STARTUP by the total number of bytes + // lost in STARTUP divided by CWND. + startupRateReductionMultiplier int64 + // Sum of bytes lost in STARTUP. + startupBytesLost congestion.ByteCount + // When true, add the most recent ack aggregation measurement during STARTUP. + enableAckAggregationDuringStartup bool + // When true, expire the windowed ack aggregation values in STARTUP when + // bandwidth increases more than 25%. + expireAckAggregationInStartup bool + // If true, will not exit low gain mode until bytes_in_flight drops below BDP + // or it's time for high gain mode. + drainToTarget bool + // If true, use a CWND of 0.75*BDP during probe_rtt instead of 4 packets. + probeRttBasedOnBdp bool + // If true, skip probe_rtt and update the timestamp of the existing min_rtt to + // now if min_rtt over the last cycle is within 12.5% of the current min_rtt. + // Even if the min_rtt is 12.5% too low, the 25% gain cycling and 2x CWND gain + // should overcome an overly small min_rtt. + probeRttSkippedIfSimilarRtt bool + // If true, disable PROBE_RTT entirely as long as the connection was recently + // app limited. + probeRttDisabledIfAppLimited bool + appLimitedSinceLastProbeRtt bool + minRttSinceLastProbeRtt time.Duration + // Latched value of --quic_always_get_bw_sample_when_acked. + alwaysGetBwSampleWhenAcked bool + + pacer *pacer + maxDatagramSize congestion.ByteCount +} + +func NewBBRSender(clock Clock, initialMaxDatagramSize, initialCongestionWindow, maxCongestionWindow congestion.ByteCount) *bbrSender { + b := &bbrSender{ + mode: STARTUP, + clock: clock, + sampler: NewBandwidthSampler(), + maxBandwidth: NewWindowedFilter(int64(BandwidthWindowSize), MaxFilter), + maxAckHeight: NewWindowedFilter(int64(BandwidthWindowSize), MaxFilter), + congestionWindow: initialCongestionWindow, + initialCongestionWindow: initialCongestionWindow, + maxCongestionWindow: maxCongestionWindow, + minCongestionWindow: DefaultMinimumCongestionWindow, + highGain: DefaultHighGain, + highCwndGain: DefaultHighGain, + drainGain: 1.0 / DefaultHighGain, + pacingGain: 1.0, + congestionWindowGain: 1.0, + congestionWindowGainConst: DefaultCongestionWindowGainConst, + numStartupRtts: RoundTripsWithoutGrowthBeforeExitingStartup, + recoveryState: NOT_IN_RECOVERY, + recoveryWindow: maxCongestionWindow, + minRttSinceLastProbeRtt: InfiniteRTT, + maxDatagramSize: initialMaxDatagramSize, + } + b.pacer = newPacer(b.BandwidthEstimate) + return b +} + +func (b *bbrSender) SetRTTStatsProvider(provider congestion.RTTStatsProvider) { + b.rttStats = provider +} + +func (b *bbrSender) GetBytesInFlight() congestion.ByteCount { + return b.bytesInFlight +} + +// TimeUntilSend returns when the next packet should be sent. +func (b *bbrSender) TimeUntilSend(bytesInFlight congestion.ByteCount) time.Time { + b.bytesInFlight = bytesInFlight + return b.pacer.TimeUntilSend() +} + +func (b *bbrSender) HasPacingBudget() bool { + return b.pacer.Budget(b.clock.Now()) >= b.maxDatagramSize +} + +func (b *bbrSender) SetMaxDatagramSize(s congestion.ByteCount) { + if s < b.maxDatagramSize { + panic(fmt.Sprintf("congestion BUG: decreased max datagram size from %d to %d", b.maxDatagramSize, s)) + } + cwndIsMinCwnd := b.congestionWindow == b.minCongestionWindow + b.maxDatagramSize = s + if cwndIsMinCwnd { + b.congestionWindow = b.minCongestionWindow + } + b.pacer.SetMaxDatagramSize(s) +} + +func (b *bbrSender) OnPacketSent(sentTime time.Time, bytesInFlight congestion.ByteCount, packetNumber congestion.PacketNumber, bytes congestion.ByteCount, isRetransmittable bool) { + b.pacer.SentPacket(sentTime, bytes) + b.lastSendPacket = packetNumber + + b.bytesInFlight = bytesInFlight + if bytesInFlight == 0 && b.sampler.isAppLimited { + b.exitingQuiescence = true + } + + if b.aggregationEpochStartTime.IsZero() { + b.aggregationEpochStartTime = sentTime + } + + b.sampler.OnPacketSent(sentTime, packetNumber, bytes, bytesInFlight, isRetransmittable) +} + +func (b *bbrSender) CanSend(bytesInFlight congestion.ByteCount) bool { + b.bytesInFlight = bytesInFlight + return bytesInFlight < b.GetCongestionWindow() +} + +func (b *bbrSender) GetCongestionWindow() congestion.ByteCount { + if b.mode == PROBE_RTT { + return b.ProbeRttCongestionWindow() + } + + if b.InRecovery() && !(b.rateBasedStartup && b.mode == STARTUP) { + return minByteCount(b.congestionWindow, b.recoveryWindow) + } + + return b.congestionWindow +} + +func (b *bbrSender) MaybeExitSlowStart() { + +} + +func (b *bbrSender) OnPacketAcked(number congestion.PacketNumber, ackedBytes congestion.ByteCount, priorInFlight congestion.ByteCount, eventTime time.Time) { + totalBytesAckedBefore := b.sampler.totalBytesAcked + isRoundStart, minRttExpired := false, false + lastAckedPacket := number + + isRoundStart = b.UpdateRoundTripCounter(lastAckedPacket) + minRttExpired = b.UpdateBandwidthAndMinRtt(eventTime, number, ackedBytes) + b.UpdateRecoveryState(false, isRoundStart) + bytesAcked := b.sampler.totalBytesAcked - totalBytesAckedBefore + excessAcked := b.UpdateAckAggregationBytes(eventTime, bytesAcked) + + // Handle logic specific to STARTUP and DRAIN modes. + if isRoundStart && !b.isAtFullBandwidth { + b.CheckIfFullBandwidthReached() + } + b.MaybeExitStartupOrDrain(eventTime) + + // Handle logic specific to PROBE_RTT. + b.MaybeEnterOrExitProbeRtt(eventTime, isRoundStart, minRttExpired) + + // After the model is updated, recalculate the pacing rate and congestion + // window. + b.CalculatePacingRate() + b.CalculateCongestionWindow(bytesAcked, excessAcked) + b.CalculateRecoveryWindow(bytesAcked, congestion.ByteCount(0)) +} + +func (b *bbrSender) OnPacketLost(number congestion.PacketNumber, lostBytes congestion.ByteCount, priorInFlight congestion.ByteCount) { + eventTime := time.Now() + totalBytesAckedBefore := b.sampler.totalBytesAcked + isRoundStart, minRttExpired := false, false + + b.DiscardLostPackets(number, lostBytes) + + // Input the new data into the BBR model of the connection. + var excessAcked congestion.ByteCount + + // Handle logic specific to PROBE_BW mode. + if b.mode == PROBE_BW { + b.UpdateGainCyclePhase(time.Now(), priorInFlight, true) + } + + // Handle logic specific to STARTUP and DRAIN modes. + b.MaybeExitStartupOrDrain(eventTime) + + // Handle logic specific to PROBE_RTT. + b.MaybeEnterOrExitProbeRtt(eventTime, isRoundStart, minRttExpired) + + // Calculate number of packets acked and lost. + bytesAcked := b.sampler.totalBytesAcked - totalBytesAckedBefore + bytesLost := lostBytes + + // After the model is updated, recalculate the pacing rate and congestion + // window. + b.CalculatePacingRate() + b.CalculateCongestionWindow(bytesAcked, excessAcked) + b.CalculateRecoveryWindow(bytesAcked, bytesLost) +} + +//func (b *bbrSender) OnCongestionEvent(priorInFlight congestion.ByteCount, eventTime time.Time, ackedPackets, lostPackets []*congestion.Packet) { +// totalBytesAckedBefore := b.sampler.totalBytesAcked +// isRoundStart, minRttExpired := false, false +// +// if lostPackets != nil { +// b.DiscardLostPackets(lostPackets) +// } +// +// // Input the new data into the BBR model of the connection. +// var excessAcked congestion.ByteCount +// if len(ackedPackets) > 0 { +// lastAckedPacket := ackedPackets[len(ackedPackets)-1].PacketNumber +// isRoundStart = b.UpdateRoundTripCounter(lastAckedPacket) +// minRttExpired = b.UpdateBandwidthAndMinRtt(eventTime, ackedPackets) +// b.UpdateRecoveryState(lastAckedPacket, len(lostPackets) > 0, isRoundStart) +// bytesAcked := b.sampler.totalBytesAcked - totalBytesAckedBefore +// excessAcked = b.UpdateAckAggregationBytes(eventTime, bytesAcked) +// } +// +// // Handle logic specific to PROBE_BW mode. +// if b.mode == PROBE_BW { +// b.UpdateGainCyclePhase(eventTime, priorInFlight, len(lostPackets) > 0) +// } +// +// // Handle logic specific to STARTUP and DRAIN modes. +// if isRoundStart && !b.isAtFullBandwidth { +// b.CheckIfFullBandwidthReached() +// } +// b.MaybeExitStartupOrDrain(eventTime) +// +// // Handle logic specific to PROBE_RTT. +// b.MaybeEnterOrExitProbeRtt(eventTime, isRoundStart, minRttExpired) +// +// // Calculate number of packets acked and lost. +// bytesAcked := b.sampler.totalBytesAcked - totalBytesAckedBefore +// bytesLost := congestion.ByteCount(0) +// for _, packet := range lostPackets { +// bytesLost += packet.Length +// } +// +// // After the model is updated, recalculate the pacing rate and congestion +// // window. +// b.CalculatePacingRate() +// b.CalculateCongestionWindow(bytesAcked, excessAcked) +// b.CalculateRecoveryWindow(bytesAcked, bytesLost) +//} + +//func (b *bbrSender) SetNumEmulatedConnections(n int) { +// +//} + +func (b *bbrSender) OnRetransmissionTimeout(packetsRetransmitted bool) { + +} + +//func (b *bbrSender) OnConnectionMigration() { +// +//} + +//// Experiments +//func (b *bbrSender) SetSlowStartLargeReduction(enabled bool) { +// +//} + +func (b *bbrSender) BandwidthEstimate() Bandwidth { + return Bandwidth(b.maxBandwidth.GetBest()) +} + +//func (b *bbrSender) HybridSlowStart() *HybridSlowStart { +// return nil +//} + +//func (b *bbrSender) SlowstartThreshold() congestion.ByteCount { +// return 0 +//} + +//func (b *bbrSender) RenoBeta() float32 { +// return 0.0 +//} + +func (b *bbrSender) InRecovery() bool { + return b.recoveryState != NOT_IN_RECOVERY +} + +func (b *bbrSender) InSlowStart() bool { + return b.mode == STARTUP +} + +//func (b *bbrSender) ShouldSendProbingPacket() bool { +// if b.pacingGain <= 1 { +// return false +// } +// // TODO(b/77975811): If the pipe is highly under-utilized, consider not +// // sending a probing transmission, because the extra bandwidth is not needed. +// // If flexible_app_limited is enabled, check if the pipe is sufficiently full. +// if b.flexibleAppLimited { +// return !b.IsPipeSufficientlyFull() +// } else { +// return true +// } +//} + +//func (b *bbrSender) IsPipeSufficientlyFull() bool { +// // See if we need more bytes in flight to see more bandwidth. +// if b.mode == STARTUP { +// // STARTUP exits if it doesn't observe a 25% bandwidth increase, so the CWND +// // must be more than 25% above the target. +// return b.GetBytesInFlight() >= b.GetTargetCongestionWindow(1.5) +// } +// if b.pacingGain > 1 { +// // Super-unity PROBE_BW doesn't exit until 1.25 * BDP is achieved. +// return b.GetBytesInFlight() >= b.GetTargetCongestionWindow(b.pacingGain) +// } +// // If bytes_in_flight are above the target congestion window, it should be +// // possible to observe the same or more bandwidth if it's available. +// return b.GetBytesInFlight() >= b.GetTargetCongestionWindow(1.1) +//} + +//func (b *bbrSender) SetFromConfig() { +// // TODO: not impl. +//} + +func (b *bbrSender) UpdateRoundTripCounter(lastAckedPacket congestion.PacketNumber) bool { + if b.currentRoundTripEnd == 0 || lastAckedPacket > b.currentRoundTripEnd { + b.currentRoundTripEnd = lastAckedPacket + b.roundTripCount++ + // if b.rttStats != nil && b.InSlowStart() { + // TODO: ++stats_->slowstart_num_rtts; + // } + return true + } + return false +} + +func (b *bbrSender) UpdateBandwidthAndMinRtt(now time.Time, number congestion.PacketNumber, ackedBytes congestion.ByteCount) bool { + sampleMinRtt := InfiniteRTT + + if !b.alwaysGetBwSampleWhenAcked && ackedBytes == 0 { + // Skip acked packets with 0 in flight bytes when updating bandwidth. + return false + } + bandwidthSample := b.sampler.OnPacketAcked(now, number) + if b.alwaysGetBwSampleWhenAcked && !bandwidthSample.stateAtSend.isValid { + // From the sampler's perspective, the packet has never been sent, or the + // packet has been acked or marked as lost previously. + return false + } + b.lastSampleIsAppLimited = bandwidthSample.stateAtSend.isAppLimited + // has_non_app_limited_sample_ |= + // !bandwidth_sample.state_at_send.is_app_limited; + if !bandwidthSample.stateAtSend.isAppLimited { + b.hasNoAppLimitedSample = true + } + if bandwidthSample.rtt > 0 { + sampleMinRtt = minRtt(sampleMinRtt, bandwidthSample.rtt) + } + if !bandwidthSample.stateAtSend.isAppLimited || bandwidthSample.bandwidth > b.BandwidthEstimate() { + b.maxBandwidth.Update(int64(bandwidthSample.bandwidth), b.roundTripCount) + } + + // If none of the RTT samples are valid, return immediately. + if sampleMinRtt == InfiniteRTT { + return false + } + + b.minRttSinceLastProbeRtt = minRtt(b.minRttSinceLastProbeRtt, sampleMinRtt) + // Do not expire min_rtt if none was ever available. + minRttExpired := b.minRtt > 0 && (now.After(b.minRttTimestamp.Add(MinRttExpiry))) + if minRttExpired || sampleMinRtt < b.minRtt || b.minRtt == 0 { + if minRttExpired && b.ShouldExtendMinRttExpiry() { + minRttExpired = false + } else { + b.minRtt = sampleMinRtt + } + b.minRttTimestamp = now + // Reset since_last_probe_rtt fields. + b.minRttSinceLastProbeRtt = InfiniteRTT + b.appLimitedSinceLastProbeRtt = false + } + + return minRttExpired +} + +func (b *bbrSender) ShouldExtendMinRttExpiry() bool { + if b.probeRttDisabledIfAppLimited && b.appLimitedSinceLastProbeRtt { + // Extend the current min_rtt if we've been app limited recently. + return true + } + + minRttIncreasedSinceLastProbe := b.minRttSinceLastProbeRtt > time.Duration(float64(b.minRtt)*SimilarMinRttThreshold) + if b.probeRttSkippedIfSimilarRtt && b.appLimitedSinceLastProbeRtt && !minRttIncreasedSinceLastProbe { + // Extend the current min_rtt if we've been app limited recently and an rtt + // has been measured in that time that's less than 12.5% more than the + // current min_rtt. + return true + } + + return false +} + +func (b *bbrSender) DiscardLostPackets(number congestion.PacketNumber, lostBytes congestion.ByteCount) { + b.sampler.OnPacketLost(number) + if b.mode == STARTUP { + // if b.rttStats != nil { + // TODO: slow start. + // } + if b.startupRateReductionMultiplier != 0 { + b.startupBytesLost += lostBytes + } + } +} + +func (b *bbrSender) UpdateRecoveryState(hasLosses, isRoundStart bool) { + // Exit recovery when there are no losses for a round. + if !hasLosses { + b.endRecoveryAt = b.lastSendPacket + } + switch b.recoveryState { + case NOT_IN_RECOVERY: + // Enter conservation on the first loss. + if hasLosses { + b.recoveryState = CONSERVATION + // This will cause the |recovery_window_| to be set to the correct + // value in CalculateRecoveryWindow(). + b.recoveryWindow = 0 + // Since the conservation phase is meant to be lasting for a whole + // round, extend the current round as if it were started right now. + b.currentRoundTripEnd = b.lastSendPacket + if false && b.lastSampleIsAppLimited { + b.isAppLimitedRecovery = true + } + } + case CONSERVATION: + if isRoundStart { + b.recoveryState = GROWTH + } + fallthrough + case GROWTH: + // Exit recovery if appropriate. + if !hasLosses && b.lastSendPacket > b.endRecoveryAt { + b.recoveryState = NOT_IN_RECOVERY + b.isAppLimitedRecovery = false + } + } + + if b.recoveryState != NOT_IN_RECOVERY && b.isAppLimitedRecovery { + b.sampler.OnAppLimited() + } +} + +func (b *bbrSender) UpdateAckAggregationBytes(ackTime time.Time, ackedBytes congestion.ByteCount) congestion.ByteCount { + // Compute how many bytes are expected to be delivered, assuming max bandwidth + // is correct. + expectedAckedBytes := congestion.ByteCount(b.maxBandwidth.GetBest()) * + congestion.ByteCount((ackTime.Sub(b.aggregationEpochStartTime))) + // Reset the current aggregation epoch as soon as the ack arrival rate is less + // than or equal to the max bandwidth. + if b.aggregationEpochBytes <= expectedAckedBytes { + // Reset to start measuring a new aggregation epoch. + b.aggregationEpochBytes = ackedBytes + b.aggregationEpochStartTime = ackTime + return 0 + } + // Compute how many extra bytes were delivered vs max bandwidth. + // Include the bytes most recently acknowledged to account for stretch acks. + b.aggregationEpochBytes += ackedBytes + b.maxAckHeight.Update(int64(b.aggregationEpochBytes-expectedAckedBytes), b.roundTripCount) + return b.aggregationEpochBytes - expectedAckedBytes +} + +func (b *bbrSender) UpdateGainCyclePhase(now time.Time, priorInFlight congestion.ByteCount, hasLosses bool) { + bytesInFlight := b.GetBytesInFlight() + // In most cases, the cycle is advanced after an RTT passes. + shouldAdvanceGainCycling := now.Sub(b.lastCycleStart) > b.GetMinRtt() + + // If the pacing gain is above 1.0, the connection is trying to probe the + // bandwidth by increasing the number of bytes in flight to at least + // pacing_gain * BDP. Make sure that it actually reaches the target, as long + // as there are no losses suggesting that the buffers are not able to hold + // that much. + if b.pacingGain > 1.0 && !hasLosses && priorInFlight < b.GetTargetCongestionWindow(b.pacingGain) { + shouldAdvanceGainCycling = false + } + // If pacing gain is below 1.0, the connection is trying to drain the extra + // queue which could have been incurred by probing prior to it. If the number + // of bytes in flight falls down to the estimated BDP value earlier, conclude + // that the queue has been successfully drained and exit this cycle early. + if b.pacingGain < 1.0 && bytesInFlight <= b.GetTargetCongestionWindow(1.0) { + shouldAdvanceGainCycling = true + } + + if shouldAdvanceGainCycling { + b.cycleCurrentOffset = (b.cycleCurrentOffset + 1) % GainCycleLength + b.lastCycleStart = now + // Stay in low gain mode until the target BDP is hit. + // Low gain mode will be exited immediately when the target BDP is achieved. + if b.drainToTarget && b.pacingGain < 1.0 && PacingGain[b.cycleCurrentOffset] == 1.0 && + bytesInFlight > b.GetTargetCongestionWindow(1.0) { + return + } + b.pacingGain = PacingGain[b.cycleCurrentOffset] + } +} + +func (b *bbrSender) GetTargetCongestionWindow(gain float64) congestion.ByteCount { + bdp := congestion.ByteCount(b.GetMinRtt()) * congestion.ByteCount(b.BandwidthEstimate()) + congestionWindow := congestion.ByteCount(gain * float64(bdp)) + + // BDP estimate will be zero if no bandwidth samples are available yet. + if congestionWindow == 0 { + congestionWindow = congestion.ByteCount(gain * float64(b.initialCongestionWindow)) + } + + return maxByteCount(congestionWindow, b.minCongestionWindow) +} + +func (b *bbrSender) CheckIfFullBandwidthReached() { + if b.lastSampleIsAppLimited { + return + } + + target := Bandwidth(float64(b.bandwidthAtLastRound) * StartupGrowthTarget) + if b.BandwidthEstimate() >= target { + b.bandwidthAtLastRound = b.BandwidthEstimate() + b.roundsWithoutBandwidthGain = 0 + if b.expireAckAggregationInStartup { + // Expire old excess delivery measurements now that bandwidth increased. + b.maxAckHeight.Reset(0, b.roundTripCount) + } + return + } + b.roundsWithoutBandwidthGain++ + if b.roundsWithoutBandwidthGain >= b.numStartupRtts || (b.exitStartupOnLoss && b.InRecovery()) { + b.isAtFullBandwidth = true + } +} + +func (b *bbrSender) MaybeExitStartupOrDrain(now time.Time) { + if b.mode == STARTUP && b.isAtFullBandwidth { + b.OnExitStartup(now) + b.mode = DRAIN + b.pacingGain = b.drainGain + b.congestionWindowGain = b.highCwndGain + } + if b.mode == DRAIN && b.GetBytesInFlight() <= b.GetTargetCongestionWindow(1) { + b.EnterProbeBandwidthMode(now) + } +} + +func (b *bbrSender) EnterProbeBandwidthMode(now time.Time) { + b.mode = PROBE_BW + b.congestionWindowGain = b.congestionWindowGainConst + + // Pick a random offset for the gain cycle out of {0, 2..7} range. 1 is + // excluded because in that case increased gain and decreased gain would not + // follow each other. + b.cycleCurrentOffset = rand.Int() % (GainCycleLength - 1) + if b.cycleCurrentOffset >= 1 { + b.cycleCurrentOffset += 1 + } + + b.lastCycleStart = now + b.pacingGain = PacingGain[b.cycleCurrentOffset] +} + +func (b *bbrSender) MaybeEnterOrExitProbeRtt(now time.Time, isRoundStart, minRttExpired bool) { + if minRttExpired && !b.exitingQuiescence && b.mode != PROBE_RTT { + if b.InSlowStart() { + b.OnExitStartup(now) + } + b.mode = PROBE_RTT + b.pacingGain = 1.0 + // Do not decide on the time to exit PROBE_RTT until the |bytes_in_flight| + // is at the target small value. + b.exitProbeRttAt = time.Time{} + } + + if b.mode == PROBE_RTT { + b.sampler.OnAppLimited() + if b.exitProbeRttAt.IsZero() { + // If the window has reached the appropriate size, schedule exiting + // PROBE_RTT. The CWND during PROBE_RTT is kMinimumCongestionWindow, but + // we allow an extra packet since QUIC checks CWND before sending a + // packet. + if b.GetBytesInFlight() < b.ProbeRttCongestionWindow()+MaxOutgoingPacketSize { + b.exitProbeRttAt = now.Add(ProbeRttTime) + b.probeRttRoundPassed = false + } + } else { + if isRoundStart { + b.probeRttRoundPassed = true + } + if !now.Before(b.exitProbeRttAt) && b.probeRttRoundPassed { + b.minRttTimestamp = now + if !b.isAtFullBandwidth { + b.EnterStartupMode(now) + } else { + b.EnterProbeBandwidthMode(now) + } + } + } + } + b.exitingQuiescence = false +} + +func (b *bbrSender) ProbeRttCongestionWindow() congestion.ByteCount { + if b.probeRttBasedOnBdp { + return b.GetTargetCongestionWindow(ModerateProbeRttMultiplier) + } else { + return b.minCongestionWindow + } +} + +func (b *bbrSender) EnterStartupMode(now time.Time) { + // if b.rttStats != nil { + // TODO: slow start. + // } + b.mode = STARTUP + b.pacingGain = b.highGain + b.congestionWindowGain = b.highCwndGain +} + +func (b *bbrSender) OnExitStartup(now time.Time) { + if b.rttStats == nil { + return + } + // TODO: slow start. +} + +func (b *bbrSender) CalculatePacingRate() { + if b.BandwidthEstimate() == 0 { + return + } + + targetRate := Bandwidth(b.pacingGain * float64(b.BandwidthEstimate())) + if b.isAtFullBandwidth { + b.pacingRate = targetRate + return + } + + // Pace at the rate of initial_window / RTT as soon as RTT measurements are + // available. + if b.pacingRate == 0 && b.rttStats.MinRTT() > 0 { + b.pacingRate = BandwidthFromDelta(b.initialCongestionWindow, b.rttStats.MinRTT()) + return + } + // Slow the pacing rate in STARTUP once loss has ever been detected. + hasEverDetectedLoss := b.endRecoveryAt > 0 + if b.slowerStartup && hasEverDetectedLoss && b.hasNoAppLimitedSample { + b.pacingRate = Bandwidth(StartupAfterLossGain * float64(b.BandwidthEstimate())) + return + } + + // Slow the pacing rate in STARTUP by the bytes_lost / CWND. + if b.startupRateReductionMultiplier != 0 && hasEverDetectedLoss && b.hasNoAppLimitedSample { + b.pacingRate = Bandwidth((1.0 - (float64(b.startupBytesLost) * float64(b.startupRateReductionMultiplier) / float64(b.congestionWindow))) * float64(targetRate)) + // Ensure the pacing rate doesn't drop below the startup growth target times + // the bandwidth estimate. + b.pacingRate = maxBandwidth(b.pacingRate, Bandwidth(StartupGrowthTarget*float64(b.BandwidthEstimate()))) + return + } + + // Do not decrease the pacing rate during startup. + b.pacingRate = maxBandwidth(b.pacingRate, targetRate) +} + +func (b *bbrSender) CalculateCongestionWindow(ackedBytes, excessAcked congestion.ByteCount) { + if b.mode == PROBE_RTT { + return + } + + targetWindow := b.GetTargetCongestionWindow(b.congestionWindowGain) + if b.isAtFullBandwidth { + // Add the max recently measured ack aggregation to CWND. + targetWindow += congestion.ByteCount(b.maxAckHeight.GetBest()) + } else if b.enableAckAggregationDuringStartup { + // Add the most recent excess acked. Because CWND never decreases in + // STARTUP, this will automatically create a very localized max filter. + targetWindow += excessAcked + } + + // Instead of immediately setting the target CWND as the new one, BBR grows + // the CWND towards |target_window| by only increasing it |bytes_acked| at a + // time. + addBytesAcked := true || !b.InRecovery() + if b.isAtFullBandwidth { + b.congestionWindow = minByteCount(targetWindow, b.congestionWindow+ackedBytes) + } else if addBytesAcked && (b.congestionWindow < targetWindow || b.sampler.totalBytesAcked < b.initialCongestionWindow) { + // If the connection is not yet out of startup phase, do not decrease the + // window. + b.congestionWindow += ackedBytes + } + + // Enforce the limits on the congestion window. + b.congestionWindow = maxByteCount(b.congestionWindow, b.minCongestionWindow) + b.congestionWindow = minByteCount(b.congestionWindow, b.maxCongestionWindow) +} + +func (b *bbrSender) CalculateRecoveryWindow(ackedBytes, lostBytes congestion.ByteCount) { + if b.rateBasedStartup && b.mode == STARTUP { + return + } + + if b.recoveryState == NOT_IN_RECOVERY { + return + } + + // Set up the initial recovery window. + if b.recoveryWindow == 0 { + b.recoveryWindow = maxByteCount(b.GetBytesInFlight()+ackedBytes, b.minCongestionWindow) + return + } + + // Remove losses from the recovery window, while accounting for a potential + // integer underflow. + if b.recoveryWindow >= lostBytes { + b.recoveryWindow -= lostBytes + } else { + b.recoveryWindow = MaxSegmentSize + } + // In CONSERVATION mode, just subtracting losses is sufficient. In GROWTH, + // release additional |bytes_acked| to achieve a slow-start-like behavior. + if b.recoveryState == GROWTH { + b.recoveryWindow += ackedBytes + } + // Sanity checks. Ensure that we always allow to send at least an MSS or + // |bytes_acked| in response, whichever is larger. + b.recoveryWindow = maxByteCount(b.recoveryWindow, b.GetBytesInFlight()+ackedBytes) + b.recoveryWindow = maxByteCount(b.recoveryWindow, b.minCongestionWindow) +} + +var _ congestion.CongestionControl = &bbrSender{} + +func (b *bbrSender) GetMinRtt() time.Duration { + if b.minRtt > 0 { + return b.minRtt + } else { + return InitialRtt + } +} + +func minRtt(a, b time.Duration) time.Duration { + if a < b { + return a + } else { + return b + } +} + +func minBandwidth(a, b Bandwidth) Bandwidth { + if a < b { + return a + } else { + return b + } +} + +func maxBandwidth(a, b Bandwidth) Bandwidth { + if a > b { + return a + } else { + return b + } +} + +func maxByteCount(a, b congestion.ByteCount) congestion.ByteCount { + if a > b { + return a + } else { + return b + } +} + +func minByteCount(a, b congestion.ByteCount) congestion.ByteCount { + if a < b { + return a + } else { + return b + } +} + +var ( + InfiniteRTT = time.Duration(math.MaxInt64) +) diff --git a/transport/tuic/congestion/clock.go b/transport/tuic/congestion/clock.go new file mode 100644 index 00000000..405fae70 --- /dev/null +++ b/transport/tuic/congestion/clock.go @@ -0,0 +1,18 @@ +package congestion + +import "time" + +// A Clock returns the current time +type Clock interface { + Now() time.Time +} + +// DefaultClock implements the Clock interface using the Go stdlib clock. +type DefaultClock struct{} + +var _ Clock = DefaultClock{} + +// Now gets the current time +func (DefaultClock) Now() time.Time { + return time.Now() +} diff --git a/transport/tuic/congestion/cubic.go b/transport/tuic/congestion/cubic.go new file mode 100644 index 00000000..dd491a32 --- /dev/null +++ b/transport/tuic/congestion/cubic.go @@ -0,0 +1,213 @@ +package congestion + +import ( + "math" + "time" + + "github.com/metacubex/quic-go/congestion" +) + +// This cubic implementation is based on the one found in Chromiums's QUIC +// implementation, in the files net/quic/congestion_control/cubic.{hh,cc}. + +// Constants based on TCP defaults. +// The following constants are in 2^10 fractions of a second instead of ms to +// allow a 10 shift right to divide. + +// 1024*1024^3 (first 1024 is from 0.100^3) +// where 0.100 is 100 ms which is the scaling round trip time. +const ( + cubeScale = 40 + cubeCongestionWindowScale = 410 + cubeFactor congestion.ByteCount = 1 << cubeScale / cubeCongestionWindowScale / maxDatagramSize + // TODO: when re-enabling cubic, make sure to use the actual packet size here + maxDatagramSize = congestion.ByteCount(InitialPacketSizeIPv4) +) + +const defaultNumConnections = 1 + +// Default Cubic backoff factor +const beta float32 = 0.7 + +// Additional backoff factor when loss occurs in the concave part of the Cubic +// curve. This additional backoff factor is expected to give up bandwidth to +// new concurrent flows and speed up convergence. +const betaLastMax float32 = 0.85 + +// Cubic implements the cubic algorithm from TCP +type Cubic struct { + clock Clock + + // Number of connections to simulate. + numConnections int + + // Time when this cycle started, after last loss event. + epoch time.Time + + // Max congestion window used just before last loss event. + // Note: to improve fairness to other streams an additional back off is + // applied to this value if the new value is below our latest value. + lastMaxCongestionWindow congestion.ByteCount + + // Number of acked bytes since the cycle started (epoch). + ackedBytesCount congestion.ByteCount + + // TCP Reno equivalent congestion window in packets. + estimatedTCPcongestionWindow congestion.ByteCount + + // Origin point of cubic function. + originPointCongestionWindow congestion.ByteCount + + // Time to origin point of cubic function in 2^10 fractions of a second. + timeToOriginPoint uint32 + + // Last congestion window in packets computed by cubic function. + lastTargetCongestionWindow congestion.ByteCount +} + +// NewCubic returns a new Cubic instance +func NewCubic(clock Clock) *Cubic { + c := &Cubic{ + clock: clock, + numConnections: defaultNumConnections, + } + c.Reset() + return c +} + +// Reset is called after a timeout to reset the cubic state +func (c *Cubic) Reset() { + c.epoch = time.Time{} + c.lastMaxCongestionWindow = 0 + c.ackedBytesCount = 0 + c.estimatedTCPcongestionWindow = 0 + c.originPointCongestionWindow = 0 + c.timeToOriginPoint = 0 + c.lastTargetCongestionWindow = 0 +} + +func (c *Cubic) alpha() float32 { + // TCPFriendly alpha is described in Section 3.3 of the CUBIC paper. Note that + // beta here is a cwnd multiplier, and is equal to 1-beta from the paper. + // We derive the equivalent alpha for an N-connection emulation as: + b := c.beta() + return 3 * float32(c.numConnections) * float32(c.numConnections) * (1 - b) / (1 + b) +} + +func (c *Cubic) beta() float32 { + // kNConnectionBeta is the backoff factor after loss for our N-connection + // emulation, which emulates the effective backoff of an ensemble of N + // TCP-Reno connections on a single loss event. The effective multiplier is + // computed as: + return (float32(c.numConnections) - 1 + beta) / float32(c.numConnections) +} + +func (c *Cubic) betaLastMax() float32 { + // betaLastMax is the additional backoff factor after loss for our + // N-connection emulation, which emulates the additional backoff of + // an ensemble of N TCP-Reno connections on a single loss event. The + // effective multiplier is computed as: + return (float32(c.numConnections) - 1 + betaLastMax) / float32(c.numConnections) +} + +// OnApplicationLimited is called on ack arrival when sender is unable to use +// the available congestion window. Resets Cubic state during quiescence. +func (c *Cubic) OnApplicationLimited() { + // When sender is not using the available congestion window, the window does + // not grow. But to be RTT-independent, Cubic assumes that the sender has been + // using the entire window during the time since the beginning of the current + // "epoch" (the end of the last loss recovery period). Since + // application-limited periods break this assumption, we reset the epoch when + // in such a period. This reset effectively freezes congestion window growth + // through application-limited periods and allows Cubic growth to continue + // when the entire window is being used. + c.epoch = time.Time{} +} + +// CongestionWindowAfterPacketLoss computes a new congestion window to use after +// a loss event. Returns the new congestion window in packets. The new +// congestion window is a multiplicative decrease of our current window. +func (c *Cubic) CongestionWindowAfterPacketLoss(currentCongestionWindow congestion.ByteCount) congestion.ByteCount { + if currentCongestionWindow+maxDatagramSize < c.lastMaxCongestionWindow { + // We never reached the old max, so assume we are competing with another + // flow. Use our extra back off factor to allow the other flow to go up. + c.lastMaxCongestionWindow = congestion.ByteCount(c.betaLastMax() * float32(currentCongestionWindow)) + } else { + c.lastMaxCongestionWindow = currentCongestionWindow + } + c.epoch = time.Time{} // Reset time. + return congestion.ByteCount(float32(currentCongestionWindow) * c.beta()) +} + +// CongestionWindowAfterAck computes a new congestion window to use after a received ACK. +// Returns the new congestion window in packets. The new congestion window +// follows a cubic function that depends on the time passed since last +// packet loss. +func (c *Cubic) CongestionWindowAfterAck( + ackedBytes congestion.ByteCount, + currentCongestionWindow congestion.ByteCount, + delayMin time.Duration, + eventTime time.Time, +) congestion.ByteCount { + c.ackedBytesCount += ackedBytes + + if c.epoch.IsZero() { + // First ACK after a loss event. + c.epoch = eventTime // Start of epoch. + c.ackedBytesCount = ackedBytes // Reset count. + // Reset estimated_tcp_congestion_window_ to be in sync with cubic. + c.estimatedTCPcongestionWindow = currentCongestionWindow + if c.lastMaxCongestionWindow <= currentCongestionWindow { + c.timeToOriginPoint = 0 + c.originPointCongestionWindow = currentCongestionWindow + } else { + c.timeToOriginPoint = uint32(math.Cbrt(float64(cubeFactor * (c.lastMaxCongestionWindow - currentCongestionWindow)))) + c.originPointCongestionWindow = c.lastMaxCongestionWindow + } + } + + // Change the time unit from microseconds to 2^10 fractions per second. Take + // the round trip time in account. This is done to allow us to use shift as a + // divide operator. + elapsedTime := int64(eventTime.Add(delayMin).Sub(c.epoch)/time.Microsecond) << 10 / (1000 * 1000) + + // Right-shifts of negative, signed numbers have implementation-dependent + // behavior, so force the offset to be positive, as is done in the kernel. + offset := int64(c.timeToOriginPoint) - elapsedTime + if offset < 0 { + offset = -offset + } + + deltaCongestionWindow := congestion.ByteCount(cubeCongestionWindowScale*offset*offset*offset) * maxDatagramSize >> cubeScale + var targetCongestionWindow congestion.ByteCount + if elapsedTime > int64(c.timeToOriginPoint) { + targetCongestionWindow = c.originPointCongestionWindow + deltaCongestionWindow + } else { + targetCongestionWindow = c.originPointCongestionWindow - deltaCongestionWindow + } + // Limit the CWND increase to half the acked bytes. + targetCongestionWindow = Min(targetCongestionWindow, currentCongestionWindow+c.ackedBytesCount/2) + + // Increase the window by approximately Alpha * 1 MSS of bytes every + // time we ack an estimated tcp window of bytes. For small + // congestion windows (less than 25), the formula below will + // increase slightly slower than linearly per estimated tcp window + // of bytes. + c.estimatedTCPcongestionWindow += congestion.ByteCount(float32(c.ackedBytesCount) * c.alpha() * float32(maxDatagramSize) / float32(c.estimatedTCPcongestionWindow)) + c.ackedBytesCount = 0 + + // We have a new cubic congestion window. + c.lastTargetCongestionWindow = targetCongestionWindow + + // Compute target congestion_window based on cubic target and estimated TCP + // congestion_window, use highest (fastest). + if targetCongestionWindow < c.estimatedTCPcongestionWindow { + targetCongestionWindow = c.estimatedTCPcongestionWindow + } + return targetCongestionWindow +} + +// SetNumConnections sets the number of emulated connections +func (c *Cubic) SetNumConnections(n int) { + c.numConnections = n +} diff --git a/transport/tuic/congestion/cubic_sender.go b/transport/tuic/congestion/cubic_sender.go new file mode 100644 index 00000000..c55db7bd --- /dev/null +++ b/transport/tuic/congestion/cubic_sender.go @@ -0,0 +1,318 @@ +package congestion + +import ( + "fmt" + "time" + + "github.com/metacubex/quic-go/congestion" + "github.com/metacubex/quic-go/logging" +) + +const ( + maxBurstPackets = 3 + renoBeta = 0.7 // Reno backoff factor. + minCongestionWindowPackets = 2 + initialCongestionWindow = 32 +) + +const InvalidPacketNumber congestion.PacketNumber = -1 +const MaxCongestionWindowPackets = 20000 +const MaxByteCount = congestion.ByteCount(1<<62 - 1) + +type cubicSender struct { + hybridSlowStart HybridSlowStart + rttStats congestion.RTTStatsProvider + cubic *Cubic + pacer *pacer + clock Clock + + reno bool + + // Track the largest packet that has been sent. + largestSentPacketNumber congestion.PacketNumber + + // Track the largest packet that has been acked. + largestAckedPacketNumber congestion.PacketNumber + + // Track the largest packet number outstanding when a CWND cutback occurs. + largestSentAtLastCutback congestion.PacketNumber + + // Whether the last loss event caused us to exit slowstart. + // Used for stats collection of slowstartPacketsLost + lastCutbackExitedSlowstart bool + + // Congestion window in bytes. + congestionWindow congestion.ByteCount + + // Slow start congestion window in bytes, aka ssthresh. + slowStartThreshold congestion.ByteCount + + // ACK counter for the Reno implementation. + numAckedPackets uint64 + + initialCongestionWindow congestion.ByteCount + initialMaxCongestionWindow congestion.ByteCount + + maxDatagramSize congestion.ByteCount + + lastState logging.CongestionState + tracer logging.ConnectionTracer +} + +var ( + _ congestion.CongestionControl = &cubicSender{} +) + +// NewCubicSender makes a new cubic sender +func NewCubicSender( + clock Clock, + initialMaxDatagramSize congestion.ByteCount, + reno bool, + tracer logging.ConnectionTracer, +) *cubicSender { + return newCubicSender( + clock, + reno, + initialMaxDatagramSize, + initialCongestionWindow*initialMaxDatagramSize, + MaxCongestionWindowPackets*initialMaxDatagramSize, + tracer, + ) +} + +func newCubicSender( + clock Clock, + reno bool, + initialMaxDatagramSize, + initialCongestionWindow, + initialMaxCongestionWindow congestion.ByteCount, + tracer logging.ConnectionTracer, +) *cubicSender { + c := &cubicSender{ + largestSentPacketNumber: InvalidPacketNumber, + largestAckedPacketNumber: InvalidPacketNumber, + largestSentAtLastCutback: InvalidPacketNumber, + initialCongestionWindow: initialCongestionWindow, + initialMaxCongestionWindow: initialMaxCongestionWindow, + congestionWindow: initialCongestionWindow, + slowStartThreshold: MaxByteCount, + cubic: NewCubic(clock), + clock: clock, + reno: reno, + tracer: tracer, + maxDatagramSize: initialMaxDatagramSize, + } + c.pacer = newPacer(c.BandwidthEstimate) + if c.tracer != nil { + c.lastState = logging.CongestionStateSlowStart + c.tracer.UpdatedCongestionState(logging.CongestionStateSlowStart) + } + return c +} + +func (c *cubicSender) SetRTTStatsProvider(provider congestion.RTTStatsProvider) { + c.rttStats = provider +} + +// TimeUntilSend returns when the next packet should be sent. +func (c *cubicSender) TimeUntilSend(_ congestion.ByteCount) time.Time { + return c.pacer.TimeUntilSend() +} + +func (c *cubicSender) HasPacingBudget() bool { + return c.pacer.Budget(c.clock.Now()) >= c.maxDatagramSize +} + +func (c *cubicSender) maxCongestionWindow() congestion.ByteCount { + return c.maxDatagramSize * MaxCongestionWindowPackets +} + +func (c *cubicSender) minCongestionWindow() congestion.ByteCount { + return c.maxDatagramSize * minCongestionWindowPackets +} + +func (c *cubicSender) OnPacketSent( + sentTime time.Time, + _ congestion.ByteCount, + packetNumber congestion.PacketNumber, + bytes congestion.ByteCount, + isRetransmittable bool, +) { + c.pacer.SentPacket(sentTime, bytes) + if !isRetransmittable { + return + } + c.largestSentPacketNumber = packetNumber + c.hybridSlowStart.OnPacketSent(packetNumber) +} + +func (c *cubicSender) CanSend(bytesInFlight congestion.ByteCount) bool { + return bytesInFlight < c.GetCongestionWindow() +} + +func (c *cubicSender) InRecovery() bool { + return c.largestAckedPacketNumber != InvalidPacketNumber && c.largestAckedPacketNumber <= c.largestSentAtLastCutback +} + +func (c *cubicSender) InSlowStart() bool { + return c.GetCongestionWindow() < c.slowStartThreshold +} + +func (c *cubicSender) GetCongestionWindow() congestion.ByteCount { + return c.congestionWindow +} + +func (c *cubicSender) MaybeExitSlowStart() { + if c.InSlowStart() && + c.hybridSlowStart.ShouldExitSlowStart(c.rttStats.LatestRTT(), c.rttStats.MinRTT(), c.GetCongestionWindow()/c.maxDatagramSize) { + // exit slow start + c.slowStartThreshold = c.congestionWindow + c.maybeTraceStateChange(logging.CongestionStateCongestionAvoidance) + } +} + +func (c *cubicSender) OnPacketAcked( + ackedPacketNumber congestion.PacketNumber, + ackedBytes congestion.ByteCount, + priorInFlight congestion.ByteCount, + eventTime time.Time, +) { + c.largestAckedPacketNumber = Max(ackedPacketNumber, c.largestAckedPacketNumber) + if c.InRecovery() { + return + } + c.maybeIncreaseCwnd(ackedPacketNumber, ackedBytes, priorInFlight, eventTime) + if c.InSlowStart() { + c.hybridSlowStart.OnPacketAcked(ackedPacketNumber) + } +} + +func (c *cubicSender) OnPacketLost(packetNumber congestion.PacketNumber, lostBytes, priorInFlight congestion.ByteCount) { + // TCP NewReno (RFC6582) says that once a loss occurs, any losses in packets + // already sent should be treated as a single loss event, since it's expected. + if packetNumber <= c.largestSentAtLastCutback { + return + } + c.lastCutbackExitedSlowstart = c.InSlowStart() + c.maybeTraceStateChange(logging.CongestionStateRecovery) + + if c.reno { + c.congestionWindow = congestion.ByteCount(float64(c.congestionWindow) * renoBeta) + } else { + c.congestionWindow = c.cubic.CongestionWindowAfterPacketLoss(c.congestionWindow) + } + if minCwnd := c.minCongestionWindow(); c.congestionWindow < minCwnd { + c.congestionWindow = minCwnd + } + c.slowStartThreshold = c.congestionWindow + c.largestSentAtLastCutback = c.largestSentPacketNumber + // reset packet count from congestion avoidance mode. We start + // counting again when we're out of recovery. + c.numAckedPackets = 0 +} + +// Called when we receive an ack. Normal TCP tracks how many packets one ack +// represents, but quic has a separate ack for each packet. +func (c *cubicSender) maybeIncreaseCwnd( + _ congestion.PacketNumber, + ackedBytes congestion.ByteCount, + priorInFlight congestion.ByteCount, + eventTime time.Time, +) { + // Do not increase the congestion window unless the sender is close to using + // the current window. + if !c.isCwndLimited(priorInFlight) { + c.cubic.OnApplicationLimited() + c.maybeTraceStateChange(logging.CongestionStateApplicationLimited) + return + } + if c.congestionWindow >= c.maxCongestionWindow() { + return + } + if c.InSlowStart() { + // TCP slow start, exponential growth, increase by one for each ACK. + c.congestionWindow += c.maxDatagramSize + c.maybeTraceStateChange(logging.CongestionStateSlowStart) + return + } + // Congestion avoidance + c.maybeTraceStateChange(logging.CongestionStateCongestionAvoidance) + if c.reno { + // Classic Reno congestion avoidance. + c.numAckedPackets++ + if c.numAckedPackets >= uint64(c.congestionWindow/c.maxDatagramSize) { + c.congestionWindow += c.maxDatagramSize + c.numAckedPackets = 0 + } + } else { + c.congestionWindow = Min(c.maxCongestionWindow(), c.cubic.CongestionWindowAfterAck(ackedBytes, c.congestionWindow, c.rttStats.MinRTT(), eventTime)) + } +} + +func (c *cubicSender) isCwndLimited(bytesInFlight congestion.ByteCount) bool { + congestionWindow := c.GetCongestionWindow() + if bytesInFlight >= congestionWindow { + return true + } + availableBytes := congestionWindow - bytesInFlight + slowStartLimited := c.InSlowStart() && bytesInFlight > congestionWindow/2 + return slowStartLimited || availableBytes <= maxBurstPackets*c.maxDatagramSize +} + +// BandwidthEstimate returns the current bandwidth estimate +func (c *cubicSender) BandwidthEstimate() Bandwidth { + if c.rttStats == nil { + return infBandwidth + } + srtt := c.rttStats.SmoothedRTT() + if srtt == 0 { + // If we haven't measured an rtt, the bandwidth estimate is unknown. + return infBandwidth + } + return BandwidthFromDelta(c.GetCongestionWindow(), srtt) +} + +// OnRetransmissionTimeout is called on an retransmission timeout +func (c *cubicSender) OnRetransmissionTimeout(packetsRetransmitted bool) { + c.largestSentAtLastCutback = InvalidPacketNumber + if !packetsRetransmitted { + return + } + c.hybridSlowStart.Restart() + c.cubic.Reset() + c.slowStartThreshold = c.congestionWindow / 2 + c.congestionWindow = c.minCongestionWindow() +} + +// OnConnectionMigration is called when the connection is migrated (?) +func (c *cubicSender) OnConnectionMigration() { + c.hybridSlowStart.Restart() + c.largestSentPacketNumber = InvalidPacketNumber + c.largestAckedPacketNumber = InvalidPacketNumber + c.largestSentAtLastCutback = InvalidPacketNumber + c.lastCutbackExitedSlowstart = false + c.cubic.Reset() + c.numAckedPackets = 0 + c.congestionWindow = c.initialCongestionWindow + c.slowStartThreshold = c.initialMaxCongestionWindow +} + +func (c *cubicSender) maybeTraceStateChange(new logging.CongestionState) { + if c.tracer == nil || new == c.lastState { + return + } + c.tracer.UpdatedCongestionState(new) + c.lastState = new +} + +func (c *cubicSender) SetMaxDatagramSize(s congestion.ByteCount) { + if s < c.maxDatagramSize { + panic(fmt.Sprintf("congestion BUG: decreased max datagram size from %d to %d", c.maxDatagramSize, s)) + } + cwndIsMinCwnd := c.congestionWindow == c.minCongestionWindow() + c.maxDatagramSize = s + if cwndIsMinCwnd { + c.congestionWindow = c.minCongestionWindow() + } + c.pacer.SetMaxDatagramSize(s) +} diff --git a/transport/tuic/congestion/hybrid_slow_start.go b/transport/tuic/congestion/hybrid_slow_start.go new file mode 100644 index 00000000..7586774e --- /dev/null +++ b/transport/tuic/congestion/hybrid_slow_start.go @@ -0,0 +1,112 @@ +package congestion + +import ( + "time" + + "github.com/metacubex/quic-go/congestion" +) + +// Note(pwestin): the magic clamping numbers come from the original code in +// tcp_cubic.c. +const hybridStartLowWindow = congestion.ByteCount(16) + +// Number of delay samples for detecting the increase of delay. +const hybridStartMinSamples = uint32(8) + +// Exit slow start if the min rtt has increased by more than 1/8th. +const hybridStartDelayFactorExp = 3 // 2^3 = 8 +// The original paper specifies 2 and 8ms, but those have changed over time. +const ( + hybridStartDelayMinThresholdUs = int64(4000) + hybridStartDelayMaxThresholdUs = int64(16000) +) + +// HybridSlowStart implements the TCP hybrid slow start algorithm +type HybridSlowStart struct { + endPacketNumber congestion.PacketNumber + lastSentPacketNumber congestion.PacketNumber + started bool + currentMinRTT time.Duration + rttSampleCount uint32 + hystartFound bool +} + +// StartReceiveRound is called for the start of each receive round (burst) in the slow start phase. +func (s *HybridSlowStart) StartReceiveRound(lastSent congestion.PacketNumber) { + s.endPacketNumber = lastSent + s.currentMinRTT = 0 + s.rttSampleCount = 0 + s.started = true +} + +// IsEndOfRound returns true if this ack is the last packet number of our current slow start round. +func (s *HybridSlowStart) IsEndOfRound(ack congestion.PacketNumber) bool { + return s.endPacketNumber < ack +} + +// ShouldExitSlowStart should be called on every new ack frame, since a new +// RTT measurement can be made then. +// rtt: the RTT for this ack packet. +// minRTT: is the lowest delay (RTT) we have seen during the session. +// congestionWindow: the congestion window in packets. +func (s *HybridSlowStart) ShouldExitSlowStart(latestRTT time.Duration, minRTT time.Duration, congestionWindow congestion.ByteCount) bool { + if !s.started { + // Time to start the hybrid slow start. + s.StartReceiveRound(s.lastSentPacketNumber) + } + if s.hystartFound { + return true + } + // Second detection parameter - delay increase detection. + // Compare the minimum delay (s.currentMinRTT) of the current + // burst of packets relative to the minimum delay during the session. + // Note: we only look at the first few(8) packets in each burst, since we + // only want to compare the lowest RTT of the burst relative to previous + // bursts. + s.rttSampleCount++ + if s.rttSampleCount <= hybridStartMinSamples { + if s.currentMinRTT == 0 || s.currentMinRTT > latestRTT { + s.currentMinRTT = latestRTT + } + } + // We only need to check this once per round. + if s.rttSampleCount == hybridStartMinSamples { + // Divide minRTT by 8 to get a rtt increase threshold for exiting. + minRTTincreaseThresholdUs := int64(minRTT / time.Microsecond >> hybridStartDelayFactorExp) + // Ensure the rtt threshold is never less than 2ms or more than 16ms. + minRTTincreaseThresholdUs = Min(minRTTincreaseThresholdUs, hybridStartDelayMaxThresholdUs) + minRTTincreaseThreshold := time.Duration(Max(minRTTincreaseThresholdUs, hybridStartDelayMinThresholdUs)) * time.Microsecond + + if s.currentMinRTT > (minRTT + minRTTincreaseThreshold) { + s.hystartFound = true + } + } + // Exit from slow start if the cwnd is greater than 16 and + // increasing delay is found. + return congestionWindow >= hybridStartLowWindow && s.hystartFound +} + +// OnPacketSent is called when a packet was sent +func (s *HybridSlowStart) OnPacketSent(packetNumber congestion.PacketNumber) { + s.lastSentPacketNumber = packetNumber +} + +// OnPacketAcked gets invoked after ShouldExitSlowStart, so it's best to end +// the round when the final packet of the burst is received and start it on +// the next incoming ack. +func (s *HybridSlowStart) OnPacketAcked(ackedPacketNumber congestion.PacketNumber) { + if s.IsEndOfRound(ackedPacketNumber) { + s.started = false + } +} + +// Started returns true if started +func (s *HybridSlowStart) Started() bool { + return s.started +} + +// Restart the slow start phase +func (s *HybridSlowStart) Restart() { + s.started = false + s.hystartFound = false +} diff --git a/transport/tuic/congestion/minmax.go b/transport/tuic/congestion/minmax.go new file mode 100644 index 00000000..ed75072e --- /dev/null +++ b/transport/tuic/congestion/minmax.go @@ -0,0 +1,72 @@ +package congestion + +import ( + "math" + "time" + + "golang.org/x/exp/constraints" +) + +// InfDuration is a duration of infinite length +const InfDuration = time.Duration(math.MaxInt64) + +func Max[T constraints.Ordered](a, b T) T { + if a < b { + return b + } + return a +} + +func Min[T constraints.Ordered](a, b T) T { + if a < b { + return a + } + return b +} + +// MinNonZeroDuration return the minimum duration that's not zero. +func MinNonZeroDuration(a, b time.Duration) time.Duration { + if a == 0 { + return b + } + if b == 0 { + return a + } + return Min(a, b) +} + +// AbsDuration returns the absolute value of a time duration +func AbsDuration(d time.Duration) time.Duration { + if d >= 0 { + return d + } + return -d +} + +// MinTime returns the earlier time +func MinTime(a, b time.Time) time.Time { + if a.After(b) { + return b + } + return a +} + +// MinNonZeroTime returns the earlist time that is not time.Time{} +// If both a and b are time.Time{}, it returns time.Time{} +func MinNonZeroTime(a, b time.Time) time.Time { + if a.IsZero() { + return b + } + if b.IsZero() { + return a + } + return MinTime(a, b) +} + +// MaxTime returns the later time +func MaxTime(a, b time.Time) time.Time { + if a.After(b) { + return a + } + return b +} diff --git a/transport/tuic/congestion/pacer.go b/transport/tuic/congestion/pacer.go new file mode 100644 index 00000000..f60ef5fe --- /dev/null +++ b/transport/tuic/congestion/pacer.go @@ -0,0 +1,79 @@ +package congestion + +import ( + "math" + "time" + + "github.com/metacubex/quic-go/congestion" +) + +const initialMaxDatagramSize = congestion.ByteCount(1252) +const MinPacingDelay = time.Millisecond +const TimerGranularity = time.Millisecond +const maxBurstSizePackets = 10 + +// The pacer implements a token bucket pacing algorithm. +type pacer struct { + budgetAtLastSent congestion.ByteCount + maxDatagramSize congestion.ByteCount + lastSentTime time.Time + getAdjustedBandwidth func() uint64 // in bytes/s +} + +func newPacer(getBandwidth func() Bandwidth) *pacer { + p := &pacer{ + maxDatagramSize: initialMaxDatagramSize, + getAdjustedBandwidth: func() uint64 { + // Bandwidth is in bits/s. We need the value in bytes/s. + bw := uint64(getBandwidth() / BytesPerSecond) + // Use a slightly higher value than the actual measured bandwidth. + // RTT variations then won't result in under-utilization of the congestion window. + // Ultimately, this will result in sending packets as acknowledgments are received rather than when timers fire, + // provided the congestion window is fully utilized and acknowledgments arrive at regular intervals. + return bw * 5 / 4 + }, + } + p.budgetAtLastSent = p.maxBurstSize() + return p +} + +func (p *pacer) SentPacket(sendTime time.Time, size congestion.ByteCount) { + budget := p.Budget(sendTime) + if size > budget { + p.budgetAtLastSent = 0 + } else { + p.budgetAtLastSent = budget - size + } + p.lastSentTime = sendTime +} + +func (p *pacer) Budget(now time.Time) congestion.ByteCount { + if p.lastSentTime.IsZero() { + return p.maxBurstSize() + } + budget := p.budgetAtLastSent + (congestion.ByteCount(p.getAdjustedBandwidth())*congestion.ByteCount(now.Sub(p.lastSentTime).Nanoseconds()))/1e9 + return Min(p.maxBurstSize(), budget) +} + +func (p *pacer) maxBurstSize() congestion.ByteCount { + return Max( + congestion.ByteCount(uint64((MinPacingDelay+TimerGranularity).Nanoseconds())*p.getAdjustedBandwidth())/1e9, + maxBurstSizePackets*p.maxDatagramSize, + ) +} + +// TimeUntilSend returns when the next packet should be sent. +// It returns the zero value of time.Time if a packet can be sent immediately. +func (p *pacer) TimeUntilSend() time.Time { + if p.budgetAtLastSent >= p.maxDatagramSize { + return time.Time{} + } + return p.lastSentTime.Add(Max( + MinPacingDelay, + time.Duration(math.Ceil(float64(p.maxDatagramSize-p.budgetAtLastSent)*1e9/float64(p.getAdjustedBandwidth())))*time.Nanosecond, + )) +} + +func (p *pacer) SetMaxDatagramSize(s congestion.ByteCount) { + p.maxDatagramSize = s +} diff --git a/transport/tuic/congestion/windowed_filter.go b/transport/tuic/congestion/windowed_filter.go new file mode 100644 index 00000000..4da595b9 --- /dev/null +++ b/transport/tuic/congestion/windowed_filter.go @@ -0,0 +1,132 @@ +package congestion + +// WindowedFilter Use the following to construct a windowed filter object of type T. +// For example, a min filter using QuicTime as the time type: +// +// WindowedFilter, QuicTime, QuicTime::Delta> ObjectName; +// +// A max filter using 64-bit integers as the time type: +// +// WindowedFilter, uint64_t, int64_t> ObjectName; +// +// Specifically, this template takes four arguments: +// 1. T -- type of the measurement that is being filtered. +// 2. Compare -- MinFilter or MaxFilter, depending on the type of filter +// desired. +// 3. TimeT -- the type used to represent timestamps. +// 4. TimeDeltaT -- the type used to represent continuous time intervals between +// two timestamps. Has to be the type of (a - b) if both |a| and |b| are +// of type TimeT. +type WindowedFilter struct { + // Time length of window. + windowLength int64 + estimates []Sample + comparator func(int64, int64) bool +} + +type Sample struct { + sample int64 + time int64 +} + +// Compares two values and returns true if the first is greater than or equal +// to the second. +func MaxFilter(a, b int64) bool { + return a >= b +} + +// Compares two values and returns true if the first is less than or equal +// to the second. +func MinFilter(a, b int64) bool { + return a <= b +} + +func NewWindowedFilter(windowLength int64, comparator func(int64, int64) bool) *WindowedFilter { + return &WindowedFilter{ + windowLength: windowLength, + estimates: make([]Sample, 3), + comparator: comparator, + } +} + +// Changes the window length. Does not update any current samples. +func (f *WindowedFilter) SetWindowLength(windowLength int64) { + f.windowLength = windowLength +} + +func (f *WindowedFilter) GetBest() int64 { + return f.estimates[0].sample +} + +func (f *WindowedFilter) GetSecondBest() int64 { + return f.estimates[1].sample +} + +func (f *WindowedFilter) GetThirdBest() int64 { + return f.estimates[2].sample +} + +func (f *WindowedFilter) Update(sample int64, time int64) { + if f.estimates[0].time == 0 || f.comparator(sample, f.estimates[0].sample) || (time-f.estimates[2].time) > f.windowLength { + f.Reset(sample, time) + return + } + + if f.comparator(sample, f.estimates[1].sample) { + f.estimates[1].sample = sample + f.estimates[1].time = time + f.estimates[2].sample = sample + f.estimates[2].time = time + } else if f.comparator(sample, f.estimates[2].sample) { + f.estimates[2].sample = sample + f.estimates[2].time = time + } + + // Expire and update estimates as necessary. + if time-f.estimates[0].time > f.windowLength { + // The best estimate hasn't been updated for an entire window, so promote + // second and third best estimates. + f.estimates[0].sample = f.estimates[1].sample + f.estimates[0].time = f.estimates[1].time + f.estimates[1].sample = f.estimates[2].sample + f.estimates[1].time = f.estimates[2].time + f.estimates[2].sample = sample + f.estimates[2].time = time + // Need to iterate one more time. Check if the new best estimate is + // outside the window as well, since it may also have been recorded a + // long time ago. Don't need to iterate once more since we cover that + // case at the beginning of the method. + if time-f.estimates[0].time > f.windowLength { + f.estimates[0].sample = f.estimates[1].sample + f.estimates[0].time = f.estimates[1].time + f.estimates[1].sample = f.estimates[2].sample + f.estimates[1].time = f.estimates[2].time + } + return + } + if f.estimates[1].sample == f.estimates[0].sample && time-f.estimates[1].time > f.windowLength>>2 { + // A quarter of the window has passed without a better sample, so the + // second-best estimate is taken from the second quarter of the window. + f.estimates[1].sample = sample + f.estimates[1].time = time + f.estimates[2].sample = sample + f.estimates[2].time = time + return + } + + if f.estimates[2].sample == f.estimates[1].sample && time-f.estimates[2].time > f.windowLength>>1 { + // We've passed a half of the window without a better estimate, so take + // a third-best estimate from the second half of the window. + f.estimates[2].sample = sample + f.estimates[2].time = time + } +} + +func (f *WindowedFilter) Reset(newSample int64, newTime int64) { + f.estimates[0].sample = newSample + f.estimates[0].time = newTime + f.estimates[1].sample = newSample + f.estimates[1].time = newTime + f.estimates[2].sample = newSample + f.estimates[2].time = newTime +} diff --git a/transport/tuic/protocol.go b/transport/tuic/protocol.go index b3b000b4..913f0d51 100644 --- a/transport/tuic/protocol.go +++ b/transport/tuic/protocol.go @@ -8,7 +8,7 @@ import ( "net/netip" "strconv" - "github.com/lucas-clemente/quic-go" + "github.com/metacubex/quic-go" "lukechampine.com/blake3" C "github.com/Dreamacro/clash/constant" From 30ca59dab7301d4d4d671a1444deca2012c3cbe5 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 25 Nov 2022 10:45:06 +0800 Subject: [PATCH 139/250] fix: tuic typo --- README.md | 2 +- adapter/outbound/tuic.go | 2 +- docs/config.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 417c6ac7..9a627673 100644 --- a/README.md +++ b/README.md @@ -235,7 +235,7 @@ proxies: port: 10443 type: tuic token: TOKEN - # ip: 127.0.0.1 + # ip: 127.0.0.1 # for overwriting the DNS lookup result of the server address set in option 'server' # heartbeat_interval: 10000 # alpn: [h3] # disable_sni: true diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index b4d17653..1cfba568 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -105,7 +105,7 @@ func NewTuic(option TuicOption) (*Tuic, error) { if len(option.CustomCA) > 0 { bs, err = os.ReadFile(option.CustomCA) if err != nil { - return nil, fmt.Errorf("hysteria %s load ca error: %w", addr, err) + return nil, fmt.Errorf("tuic %s load ca error: %w", addr, err) } } else if option.CustomCAString != "" { bs = []byte(option.CustomCAString) diff --git a/docs/config.yaml b/docs/config.yaml index f2d75bb5..8688564a 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -463,7 +463,7 @@ proxies: port: 10443 type: tuic token: TOKEN - # ip: 127.0.0.1 + # ip: 127.0.0.1 # for overwriting the DNS lookup result of the server address set in option 'server' # heartbeat_interval: 10000 # alpn: [h3] # disable_sni: true From 142d17ebad60728886c9c7d91ee51bea217cbddd Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 25 Nov 2022 11:04:28 +0800 Subject: [PATCH 140/250] fix: don't close tuic when read timeout --- transport/tuic/client.go | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/transport/tuic/client.go b/transport/tuic/client.go index 82ff4c9a..8f91ef63 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -214,22 +214,26 @@ func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata, dialFn f if err != nil { return nil, err } - defer func() { - t.deferQuicConn(quicConn, err) + stream, err := func() (quic.Stream, error) { + defer func() { + t.deferQuicConn(quicConn, err) + }() + buf := &bytes.Buffer{} + err = NewConnect(NewAddress(metadata)).WriteTo(buf) + if err != nil { + return nil, err + } + stream, err := quicConn.OpenStream() + if err != nil { + return nil, err + } + _, err = buf.WriteTo(stream) + if err != nil { + return nil, err + } + return stream, err }() - buf := &bytes.Buffer{} - err = NewConnect(NewAddress(metadata)).WriteTo(buf) - if err != nil { - return nil, err - } - stream, err := quicConn.OpenStream() - if err != nil { - return nil, err - } - _, err = buf.WriteTo(stream) - if err != nil { - return nil, err - } + if t.RequestTimeout > 0 { _ = stream.SetReadDeadline(time.Now().Add(time.Duration(t.RequestTimeout) * time.Millisecond)) } From cce42b4b83fc060cd53320748ea53998fd321d11 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Fri, 25 Nov 2022 11:12:22 +0800 Subject: [PATCH 141/250] fix: prefer ipv6 not working --- adapter/outbound/util.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/adapter/outbound/util.go b/adapter/outbound/util.go index a3d88a4e..b322ba21 100644 --- a/adapter/outbound/util.go +++ b/adapter/outbound/util.go @@ -83,6 +83,7 @@ func resolveUDPAddrWithPrefer(ctx context.Context, network, address string, pref return nil, err } var ip netip.Addr + var fallback netip.Addr switch prefer { case C.IPv4Only: ip, err = resolver.ResolveIPv4ProxyServerHost(ctx, host) @@ -103,13 +104,11 @@ func resolveUDPAddrWithPrefer(ctx context.Context, network, address string, pref } } } - ip = fallback } default: // C.IPv4Prefer, C.DualStack and other var ips []netip.Addr ips, err = resolver.LookupIPProxyServerHost(ctx, host) - var fallback netip.Addr if err == nil { for _, addr := range ips { if addr.Is4() { @@ -122,12 +121,13 @@ func resolveUDPAddrWithPrefer(ctx context.Context, network, address string, pref } } - if !ip.IsValid() && fallback.IsValid() { - ip = fallback - } } } + if !ip.IsValid() && fallback.IsValid() { + ip = fallback + } + if err != nil { return nil, err } From d47ce79a24d78256278990ae5fe3e6521ffb5277 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 25 Nov 2022 11:32:05 +0800 Subject: [PATCH 142/250] chore: better tuic conn close --- adapter/outbound/tuic.go | 6 ++++++ transport/tuic/client.go | 31 ++++++++++++++++++++----------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index 1cfba568..88dbf03d 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -9,6 +9,7 @@ import ( "fmt" "net" "os" + "runtime" "strconv" "sync" "time" @@ -199,6 +200,7 @@ func NewTuic(option TuicOption) (*Tuic, error) { RequestTimeout: option.RequestTimeout, } clientMap[o] = client + runtime.SetFinalizer(client, closeTuicClient) return client } @@ -214,3 +216,7 @@ func NewTuic(option TuicOption) (*Tuic, error) { getClient: getClient, }, nil } + +func closeTuicClient(client *tuic.Client) { + client.Close(nil) +} diff --git a/transport/tuic/client.go b/transport/tuic/client.go index 8f91ef63..b6bb0bf7 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -197,18 +197,26 @@ func (t *Client) deferQuicConn(quicConn quic.Connection, err error) { t.connMutex.Lock() defer t.connMutex.Unlock() if t.quicConn == quicConn { - t.udpInputMap.Range(func(key, value any) bool { - if conn, ok := value.(net.Conn); ok { - _ = conn.Close() - } - return true - }) - t.udpInputMap = sync.Map{} // new one - t.quicConn = nil + t.Close(err) } } } +func (t *Client) Close(err error) { + quicConn := t.quicConn + if quicConn != nil { + _ = t.quicConn.CloseWithError(ProtocolError, err.Error()) + t.udpInputMap.Range(func(key, value any) bool { + if conn, ok := value.(net.Conn); ok { + _ = conn.Close() + } + return true + }) + t.udpInputMap = sync.Map{} // new one + t.quicConn = nil + } +} + func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata, dialFn func(ctx context.Context) (net.PacketConn, net.Addr, error)) (net.Conn, error) { quicConn, err := t.getQuicConn(ctx, dialFn) if err != nil { @@ -237,7 +245,7 @@ func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata, dialFn f if t.RequestTimeout > 0 { _ = stream.SetReadDeadline(time.Now().Add(time.Duration(t.RequestTimeout) * time.Millisecond)) } - conn := N.NewBufferedConn(&quicStreamConn{stream, quicConn.LocalAddr(), quicConn.RemoteAddr()}) + conn := N.NewBufferedConn(&quicStreamConn{stream, quicConn.LocalAddr(), quicConn.RemoteAddr(), t}) response, err := ReadResponse(conn) if err != nil { return nil, err @@ -252,8 +260,9 @@ func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata, dialFn f type quicStreamConn struct { quic.Stream - lAddr net.Addr - rAddr net.Addr + lAddr net.Addr + rAddr net.Addr + client *Client } func (q *quicStreamConn) LocalAddr() net.Addr { From a13dedb6e4d01907ed6164e9d161fe7ad32eeebd Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 25 Nov 2022 12:10:33 +0800 Subject: [PATCH 143/250] fix: tuic panic --- transport/tuic/client.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/transport/tuic/client.go b/transport/tuic/client.go index b6bb0bf7..eaa981ab 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -241,6 +241,9 @@ func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata, dialFn f } return stream, err }() + if err != nil { + return nil, err + } if t.RequestTimeout > 0 { _ = stream.SetReadDeadline(time.Now().Add(time.Duration(t.RequestTimeout) * time.Millisecond)) From f54235140405b41010a775e9c284c92ca3d8bc4a Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 25 Nov 2022 12:43:23 +0800 Subject: [PATCH 144/250] chore: tuic add max_udp_relay_packet_size --- README.md | 1 + adapter/outbound/tuic.go | 63 ++++++++++++++++++++++++++-------------- docs/config.yaml | 1 + transport/tuic/client.go | 25 +++++++++++----- 4 files changed, 60 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 9a627673..c0f39939 100644 --- a/README.md +++ b/README.md @@ -243,6 +243,7 @@ proxies: # request_timeout: 8000 udp_relay_mode: native # Available: "native", "quic". Default: "native" # congestion_controller: bbr # Available: "cubic", "new_reno", "bbr". Default: "cubic" + # max_udp_relay_packet_size: 1500 # skip-cert-verify: true ``` diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index 88dbf03d..502a751d 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -29,18 +29,19 @@ type Tuic struct { type TuicOption struct { BasicOption - Name string `proxy:"name"` - Server string `proxy:"server"` - Port int `proxy:"port"` - Token string `proxy:"token"` - Ip string `proxy:"ip,omitempty"` - HeartbeatInterval int `proxy:"heartbeat_interval,omitempty"` - ALPN []string `proxy:"alpn,omitempty"` - ReduceRtt bool `proxy:"reduce_rtt,omitempty"` - RequestTimeout int `proxy:"request_timeout,omitempty"` - UdpRelayMode string `proxy:"udp_relay_mode,omitempty"` - CongestionController string `proxy:"congestion_controller,omitempty"` - DisableSni bool `proxy:"disable_sni,omitempty"` + Name string `proxy:"name"` + Server string `proxy:"server"` + Port int `proxy:"port"` + Token string `proxy:"token"` + Ip string `proxy:"ip,omitempty"` + HeartbeatInterval int `proxy:"heartbeat_interval,omitempty"` + ALPN []string `proxy:"alpn,omitempty"` + ReduceRtt bool `proxy:"reduce_rtt,omitempty"` + RequestTimeout int `proxy:"request_timeout,omitempty"` + UdpRelayMode string `proxy:"udp_relay_mode,omitempty"` + CongestionController string `proxy:"congestion_controller,omitempty"` + DisableSni bool `proxy:"disable_sni,omitempty"` + MaxUdpRelayPacketSize int `proxy:"max_udp_relay_packet_size,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` Fingerprint string `proxy:"fingerprint,omitempty"` @@ -152,6 +153,10 @@ func NewTuic(option TuicOption) (*Tuic, error) { option.UdpRelayMode = "native" } + if option.MaxUdpRelayPacketSize == 0 { + option.MaxUdpRelayPacketSize = 1500 + } + quicConfig := &quic.Config{ InitialStreamReceiveWindow: uint64(option.ReceiveWindowConn), MaxStreamReceiveWindow: uint64(option.ReceiveWindowConn), @@ -186,18 +191,32 @@ func NewTuic(option TuicOption) (*Tuic, error) { clientMapMutex.Lock() defer clientMapMutex.Unlock() - if client, ok := clientMap[o]; ok && client != nil { - return client + for key := range clientMap { + client := clientMap[key] + if client == nil { + delete(clientMap, key) // It is safe in Golang + continue + } + if key == o { + client.LastVisited = time.Now() + return client + } + if time.Now().Sub(client.LastVisited) > 30*time.Minute { + delete(clientMap, key) + continue + } } client := &tuic.Client{ - TlsConfig: tlsConfig, - QuicConfig: quicConfig, - Host: host, - Token: tkn, - UdpRelayMode: option.UdpRelayMode, - CongestionController: option.CongestionController, - ReduceRtt: option.ReduceRtt, - RequestTimeout: option.RequestTimeout, + TlsConfig: tlsConfig, + QuicConfig: quicConfig, + Host: host, + Token: tkn, + UdpRelayMode: option.UdpRelayMode, + CongestionController: option.CongestionController, + ReduceRtt: option.ReduceRtt, + RequestTimeout: option.RequestTimeout, + MaxUdpRelayPacketSize: option.MaxUdpRelayPacketSize, + LastVisited: time.Now(), } clientMap[o] = client runtime.SetFinalizer(client, closeTuicClient) diff --git a/docs/config.yaml b/docs/config.yaml index 8688564a..d187a32d 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -471,6 +471,7 @@ proxies: # request_timeout: 8000 udp_relay_mode: native # Available: "native", "quic". Default: "native" # congestion_controller: bbr # Available: "cubic", "new_reno", "bbr". Default: "cubic" + # max_udp_relay_packet_size: 1500 # skip-cert-verify: true # ShadowsocksR diff --git a/transport/tuic/client.go b/transport/tuic/client.go index eaa981ab..ad5cb7e9 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -6,6 +6,7 @@ import ( "context" "crypto/tls" "errors" + "fmt" "math/rand" "net" "net/netip" @@ -20,14 +21,17 @@ import ( ) type Client struct { - TlsConfig *tls.Config - QuicConfig *quic.Config - Host string - Token [32]byte - UdpRelayMode string - CongestionController string - ReduceRtt bool - RequestTimeout int + TlsConfig *tls.Config + QuicConfig *quic.Config + Host string + Token [32]byte + UdpRelayMode string + CongestionController string + ReduceRtt bool + RequestTimeout int + MaxUdpRelayPacketSize int + + LastVisited time.Time quicConn quic.Connection connMutex sync.Mutex @@ -237,6 +241,7 @@ func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata, dialFn f } _, err = buf.WriteTo(stream) if err != nil { + _ = stream.Close() return nil, err } return stream, err @@ -379,6 +384,9 @@ func (q *quicStreamPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err err } func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + if len(p) > q.client.MaxUdpRelayPacketSize { + return 0, fmt.Errorf("udp packet too large(%d > %d)", len(p), q.client.MaxUdpRelayPacketSize) + } defer func() { q.client.deferQuicConn(q.quicConn, err) }() @@ -401,6 +409,7 @@ func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err erro } _, err = buf.WriteTo(stream) if err != nil { + _ = stream.Close() return } err = stream.Close() From 9976800a359d341aeb0a21c0d92de3c7becce0ac Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 25 Nov 2022 13:03:36 +0800 Subject: [PATCH 145/250] fix: tuic err handle --- transport/tuic/client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/transport/tuic/client.go b/transport/tuic/client.go index ad5cb7e9..88f385a1 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -226,7 +226,7 @@ func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata, dialFn f if err != nil { return nil, err } - stream, err := func() (quic.Stream, error) { + stream, err := func() (stream quic.Stream, err error) { defer func() { t.deferQuicConn(quicConn, err) }() @@ -235,7 +235,7 @@ func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata, dialFn f if err != nil { return nil, err } - stream, err := quicConn.OpenStream() + stream, err = quicConn.OpenStream() if err != nil { return nil, err } From 9b1fe9f46609e46cec03b05a5c262045975008c7 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 25 Nov 2022 16:06:56 +0800 Subject: [PATCH 146/250] fix: tuic stream close --- transport/tuic/client.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/transport/tuic/client.go b/transport/tuic/client.go index 88f385a1..18e623ab 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -209,7 +209,7 @@ func (t *Client) deferQuicConn(quicConn quic.Connection, err error) { func (t *Client) Close(err error) { quicConn := t.quicConn if quicConn != nil { - _ = t.quicConn.CloseWithError(ProtocolError, err.Error()) + _ = quicConn.CloseWithError(ProtocolError, err.Error()) t.udpInputMap.Range(func(key, value any) bool { if conn, ok := value.(net.Conn); ok { _ = conn.Close() @@ -256,14 +256,15 @@ func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata, dialFn f conn := N.NewBufferedConn(&quicStreamConn{stream, quicConn.LocalAddr(), quicConn.RemoteAddr(), t}) response, err := ReadResponse(conn) if err != nil { + _ = conn.Close() return nil, err } if response.IsFailed() { - _ = stream.Close() + _ = conn.Close() return nil, errors.New("connect failed") } _ = stream.SetReadDeadline(time.Time{}) - return conn, err + return conn, nil } type quicStreamConn struct { @@ -273,6 +274,11 @@ type quicStreamConn struct { client *Client } +func (q *quicStreamConn) Close() error { + q.Stream.CancelRead(0) + return q.Stream.Close() +} + func (q *quicStreamConn) LocalAddr() net.Addr { return q.lAddr } From 76d2838721204d2c805b7e9b176afa134afe332f Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 25 Nov 2022 17:15:45 +0800 Subject: [PATCH 147/250] chore: split tuic's tcp and udp client --- adapter/outbound/tuic.go | 22 ++++++++++++++++------ transport/tuic/client.go | 20 +++++++++++++------- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index 502a751d..57b761c3 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -24,7 +24,7 @@ import ( type Tuic struct { *Base - getClient func(opts ...dialer.Option) *tuic.Client + getClient func(udp bool, opts ...dialer.Option) *tuic.Client } type TuicOption struct { @@ -55,7 +55,7 @@ type TuicOption struct { // DialContext implements C.ProxyAdapter func (t *Tuic) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { opts = t.Base.DialOptions(opts...) - conn, err := t.getClient(opts...).DialContext(ctx, metadata, func(ctx context.Context) (net.PacketConn, net.Addr, error) { + conn, err := t.getClient(false, opts...).DialContext(ctx, metadata, func(ctx context.Context) (net.PacketConn, net.Addr, error) { pc, err := dialer.ListenPacket(ctx, "udp", "", opts...) if err != nil { return nil, nil, err @@ -75,7 +75,7 @@ func (t *Tuic) DialContext(ctx context.Context, metadata *C.Metadata, opts ...di // ListenPacketContext implements C.ProxyAdapter func (t *Tuic) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { opts = t.Base.DialOptions(opts...) - pc, err := t.getClient(opts...).ListenPacketContext(ctx, metadata, func(ctx context.Context) (net.PacketConn, net.Addr, error) { + pc, err := t.getClient(true, opts...).ListenPacketContext(ctx, metadata, func(ctx context.Context) (net.PacketConn, net.Addr, error) { pc, err := dialer.ListenPacket(ctx, "udp", "", opts...) if err != nil { return nil, nil, err @@ -184,9 +184,18 @@ func NewTuic(option TuicOption) (*Tuic, error) { tlsConfig.ServerName = "" } tkn := tuic.GenTKN(option.Token) - clientMap := make(map[any]*tuic.Client) - clientMapMutex := sync.Mutex{} - getClient := func(opts ...dialer.Option) *tuic.Client { + tcpClientMap := make(map[any]*tuic.Client) + tcpClientMapMutex := &sync.Mutex{} + udpClientMap := make(map[any]*tuic.Client) + udpClientMapMutex := &sync.Mutex{} + getClient := func(udp bool, opts ...dialer.Option) *tuic.Client { + clientMap := tcpClientMap + clientMapMutex := tcpClientMapMutex + if udp { + clientMap = udpClientMap + clientMapMutex = udpClientMapMutex + } + o := *dialer.ApplyOptions(opts...) clientMapMutex.Lock() @@ -217,6 +226,7 @@ func NewTuic(option TuicOption) (*Tuic, error) { RequestTimeout: option.RequestTimeout, MaxUdpRelayPacketSize: option.MaxUdpRelayPacketSize, LastVisited: time.Now(), + UDP: udp, } clientMap[o] = client runtime.SetFinalizer(client, closeTuicClient) diff --git a/transport/tuic/client.go b/transport/tuic/client.go index 18e623ab..4293c9c4 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -32,6 +32,7 @@ type Client struct { MaxUdpRelayPacketSize int LastVisited time.Time + UDP bool quicConn quic.Connection connMutex sync.Mutex @@ -113,9 +114,7 @@ func (t *Client) getQuicConn(ctx context.Context, dialFn func(ctx context.Contex return nil } - go sendAuthentication(quicConn) - - go func(quicConn quic.Connection) (err error) { + parseUDP := func(quicConn quic.Connection) (err error) { defer func() { t.deferQuicConn(quicConn, err) }() @@ -189,7 +188,13 @@ func (t *Client) getQuicConn(ctx context.Context, dialFn func(ctx context.Contex }() } } - }(quicConn) + } + + go sendAuthentication(quicConn) + + if t.UDP { + go parseUDP(quicConn) + } t.quicConn = quicConn return quicConn, nil @@ -226,7 +231,7 @@ func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata, dialFn f if err != nil { return nil, err } - stream, err := func() (stream quic.Stream, err error) { + stream, err := func() (stream *quicStreamConn, err error) { defer func() { t.deferQuicConn(quicConn, err) }() @@ -235,10 +240,11 @@ func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata, dialFn f if err != nil { return nil, err } - stream, err = quicConn.OpenStream() + quicStream, err := quicConn.OpenStream() if err != nil { return nil, err } + stream = &quicStreamConn{quicStream, quicConn.LocalAddr(), quicConn.RemoteAddr(), t} _, err = buf.WriteTo(stream) if err != nil { _ = stream.Close() @@ -253,7 +259,7 @@ func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata, dialFn f if t.RequestTimeout > 0 { _ = stream.SetReadDeadline(time.Now().Add(time.Duration(t.RequestTimeout) * time.Millisecond)) } - conn := N.NewBufferedConn(&quicStreamConn{stream, quicConn.LocalAddr(), quicConn.RemoteAddr(), t}) + conn := N.NewBufferedConn(stream) response, err := ReadResponse(conn) if err != nil { _ = conn.Close() From 21a91e88a1fc4741af5454974463a2e7ee4df7a3 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 25 Nov 2022 18:32:30 +0800 Subject: [PATCH 148/250] fix: tuic set MaxOpenStreams --- adapter/outbound/tuic.go | 31 ++++++++++++++++++++++++++++--- transport/tuic/client.go | 24 +++++++++++++++++++----- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index 57b761c3..a7ba1f5e 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -6,6 +6,7 @@ import ( "crypto/tls" "encoding/hex" "encoding/pem" + "errors" "fmt" "net" "os" @@ -24,7 +25,8 @@ import ( type Tuic struct { *Base - getClient func(udp bool, opts ...dialer.Option) *tuic.Client + getClient func(udp bool, opts ...dialer.Option) *tuic.Client + removeClient func(udp bool, opts ...dialer.Option) } type TuicOption struct { @@ -67,6 +69,10 @@ func (t *Tuic) DialContext(ctx context.Context, metadata *C.Metadata, opts ...di return pc, addr, err }) if err != nil { + if errors.Is(err, tuic.TooManyOpenStreams) { + t.removeClient(false, opts...) + return t.DialContext(ctx, metadata, opts...) + } return nil, err } return NewConn(conn, t), err @@ -87,6 +93,10 @@ func (t *Tuic) ListenPacketContext(ctx context.Context, metadata *C.Metadata, op return pc, addr, err }) if err != nil { + if errors.Is(err, tuic.TooManyOpenStreams) { + t.removeClient(true, opts...) + return t.ListenPacketContext(ctx, metadata, opts...) + } return nil, err } return newPacketConn(pc, t), nil @@ -232,6 +242,20 @@ func NewTuic(option TuicOption) (*Tuic, error) { runtime.SetFinalizer(client, closeTuicClient) return client } + removeClient := func(udp bool, opts ...dialer.Option) { + clientMap := tcpClientMap + clientMapMutex := tcpClientMapMutex + if udp { + clientMap = udpClientMap + clientMapMutex = udpClientMapMutex + } + + o := *dialer.ApplyOptions(opts...) + + clientMapMutex.Lock() + defer clientMapMutex.Unlock() + delete(clientMap, o) + } return &Tuic{ Base: &Base{ @@ -242,10 +266,11 @@ func NewTuic(option TuicOption) (*Tuic, error) { iface: option.Interface, prefer: C.NewDNSPrefer(option.IPVersion), }, - getClient: getClient, + getClient: getClient, + removeClient: removeClient, }, nil } func closeTuicClient(client *tuic.Client) { - client.Close(nil) + client.Close(tuic.ClientClosed) } diff --git a/transport/tuic/client.go b/transport/tuic/client.go index 4293c9c4..a28e6ec2 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -11,6 +11,7 @@ import ( "net" "net/netip" "sync" + "sync/atomic" "time" "github.com/metacubex/quic-go" @@ -20,6 +21,13 @@ import ( "github.com/Dreamacro/clash/transport/tuic/congestion" ) +var ( + ClientClosed = errors.New("tuic: client closed") + TooManyOpenStreams = errors.New("tuic: too many open streams") +) + +const MaxOpenStreams = 100 - 10 + type Client struct { TlsConfig *tls.Config QuicConfig *quic.Config @@ -37,6 +45,8 @@ type Client struct { quicConn quic.Connection connMutex sync.Mutex + openStreams atomic.Int32 + udpInputMap sync.Map } @@ -137,6 +147,7 @@ func (t *Client) getQuicConn(ctx context.Context, dialFn func(ctx context.Contex } } } + stream.CancelRead(0) }() reader := bufio.NewReader(stream) packet, err := ReadPacket(reader) @@ -231,6 +242,11 @@ func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata, dialFn f if err != nil { return nil, err } + openStreams := t.openStreams.Add(1) + if openStreams >= MaxOpenStreams { + t.openStreams.Add(-1) + return nil, TooManyOpenStreams + } stream, err := func() (stream *quicStreamConn, err error) { defer func() { t.deferQuicConn(quicConn, err) @@ -281,7 +297,9 @@ type quicStreamConn struct { } func (q *quicStreamConn) Close() error { + //defer q.client.openStreams.Add(-1) q.Stream.CancelRead(0) + q.Stream.CancelWrite(0) return q.Stream.Close() } @@ -419,12 +437,8 @@ func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err erro if err != nil { return } + defer stream.Close() _, err = buf.WriteTo(stream) - if err != nil { - _ = stream.Close() - return - } - err = stream.Close() if err != nil { return } From c7bad89af36d755e2971ed875ca790af3a9c30e1 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 25 Nov 2022 19:14:09 +0800 Subject: [PATCH 149/250] fix: tuic better stream close --- transport/tuic/client.go | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/transport/tuic/client.go b/transport/tuic/client.go index a28e6ec2..d5d2664d 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -26,7 +26,7 @@ var ( TooManyOpenStreams = errors.New("tuic: too many open streams") ) -const MaxOpenStreams = 100 - 10 +const MaxOpenStreams = 100 - 90 type Client struct { TlsConfig *tls.Config @@ -230,9 +230,9 @@ func (t *Client) Close(err error) { if conn, ok := value.(net.Conn); ok { _ = conn.Close() } + t.udpInputMap.Delete(key) return true }) - t.udpInputMap = sync.Map{} // new one t.quicConn = nil } } @@ -260,7 +260,12 @@ func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata, dialFn f if err != nil { return nil, err } - stream = &quicStreamConn{quicStream, quicConn.LocalAddr(), quicConn.RemoteAddr(), t} + stream = &quicStreamConn{ + Stream: quicStream, + lAddr: quicConn.LocalAddr(), + rAddr: quicConn.RemoteAddr(), + client: t, + } _, err = buf.WriteTo(stream) if err != nil { _ = stream.Close() @@ -291,15 +296,34 @@ func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata, dialFn f type quicStreamConn struct { quic.Stream + lock sync.Mutex lAddr net.Addr rAddr net.Addr client *Client } +func (q *quicStreamConn) Write(p []byte) (n int, err error) { + q.lock.Lock() + defer q.lock.Unlock() + return q.Stream.Write(p) +} + func (q *quicStreamConn) Close() error { - //defer q.client.openStreams.Add(-1) + defer time.AfterFunc(C.DefaultTCPTimeout, func() { + q.client.openStreams.Add(-1) + }) + + // https://github.com/cloudflare/cloudflared/commit/ed2bac026db46b239699ac5ce4fcf122d7cab2cd + // Make sure a possible writer does not block the lock forever. We need it, so we can close the writer + // side of the stream safely. + _ = q.Stream.SetWriteDeadline(time.Now()) + + // This lock is eventually acquired despite Write also acquiring it, because we set a deadline to writes. + q.lock.Lock() + defer q.lock.Unlock() + + // We have to clean up the receiving stream ourselves since the Close in the bottom does not handle that. q.Stream.CancelRead(0) - q.Stream.CancelWrite(0) return q.Stream.Close() } From 7b44cde4bde72905a361621739d4515046b1add0 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 25 Nov 2022 20:14:05 +0800 Subject: [PATCH 150/250] chore: tuic use a simple client pool --- adapter/outbound/tuic.go | 127 +++++++++++++++++++++++---------------- transport/tuic/client.go | 32 ++++++++-- 2 files changed, 102 insertions(+), 57 deletions(-) diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index a7ba1f5e..30ea0485 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -17,6 +17,7 @@ import ( "github.com/metacubex/quic-go" + "github.com/Dreamacro/clash/common/generics/list" "github.com/Dreamacro/clash/component/dialer" tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" @@ -25,8 +26,8 @@ import ( type Tuic struct { *Base - getClient func(udp bool, opts ...dialer.Option) *tuic.Client - removeClient func(udp bool, opts ...dialer.Option) + newClient func(udp bool, opts ...dialer.Option) *tuic.Client + getClient func(udp bool, opts ...dialer.Option) *tuic.Client } type TuicOption struct { @@ -57,7 +58,7 @@ type TuicOption struct { // DialContext implements C.ProxyAdapter func (t *Tuic) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { opts = t.Base.DialOptions(opts...) - conn, err := t.getClient(false, opts...).DialContext(ctx, metadata, func(ctx context.Context) (net.PacketConn, net.Addr, error) { + dialFn := func(ctx context.Context) (net.PacketConn, net.Addr, error) { pc, err := dialer.ListenPacket(ctx, "udp", "", opts...) if err != nil { return nil, nil, err @@ -67,12 +68,12 @@ func (t *Tuic) DialContext(ctx context.Context, metadata *C.Metadata, opts ...di return nil, nil, err } return pc, addr, err - }) + } + conn, err := t.getClient(false, opts...).DialContext(ctx, metadata, dialFn) + if errors.Is(err, tuic.TooManyOpenStreams) { + conn, err = t.newClient(false, opts...).DialContext(ctx, metadata, dialFn) + } if err != nil { - if errors.Is(err, tuic.TooManyOpenStreams) { - t.removeClient(false, opts...) - return t.DialContext(ctx, metadata, opts...) - } return nil, err } return NewConn(conn, t), err @@ -81,7 +82,7 @@ func (t *Tuic) DialContext(ctx context.Context, metadata *C.Metadata, opts ...di // ListenPacketContext implements C.ProxyAdapter func (t *Tuic) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { opts = t.Base.DialOptions(opts...) - pc, err := t.getClient(true, opts...).ListenPacketContext(ctx, metadata, func(ctx context.Context) (net.PacketConn, net.Addr, error) { + dialFn := func(ctx context.Context) (net.PacketConn, net.Addr, error) { pc, err := dialer.ListenPacket(ctx, "udp", "", opts...) if err != nil { return nil, nil, err @@ -91,12 +92,12 @@ func (t *Tuic) ListenPacketContext(ctx context.Context, metadata *C.Metadata, op return nil, nil, err } return pc, addr, err - }) + } + pc, err := t.getClient(true, opts...).ListenPacketContext(ctx, metadata, dialFn) + if errors.Is(err, tuic.TooManyOpenStreams) { + pc, err = t.newClient(false, opts...).ListenPacketContext(ctx, metadata, dialFn) + } if err != nil { - if errors.Is(err, tuic.TooManyOpenStreams) { - t.removeClient(true, opts...) - return t.ListenPacketContext(ctx, metadata, opts...) - } return nil, err } return newPacketConn(pc, t), nil @@ -194,37 +195,23 @@ func NewTuic(option TuicOption) (*Tuic, error) { tlsConfig.ServerName = "" } tkn := tuic.GenTKN(option.Token) - tcpClientMap := make(map[any]*tuic.Client) - tcpClientMapMutex := &sync.Mutex{} - udpClientMap := make(map[any]*tuic.Client) - udpClientMapMutex := &sync.Mutex{} - getClient := func(udp bool, opts ...dialer.Option) *tuic.Client { - clientMap := tcpClientMap - clientMapMutex := tcpClientMapMutex + tcpClients := list.New[*tuic.Client]() + tcpClientsMutex := &sync.Mutex{} + udpClients := list.New[*tuic.Client]() + udpClientsMutex := &sync.Mutex{} + newClient := func(udp bool, opts ...dialer.Option) *tuic.Client { + clients := tcpClients + clientsMutex := tcpClientsMutex if udp { - clientMap = udpClientMap - clientMapMutex = udpClientMapMutex + clients = udpClients + clientsMutex = udpClientsMutex } - o := *dialer.ApplyOptions(opts...) + var o any = *dialer.ApplyOptions(opts...) + + clientsMutex.Lock() + defer clientsMutex.Unlock() - clientMapMutex.Lock() - defer clientMapMutex.Unlock() - for key := range clientMap { - client := clientMap[key] - if client == nil { - delete(clientMap, key) // It is safe in Golang - continue - } - if key == o { - client.LastVisited = time.Now() - return client - } - if time.Now().Sub(client.LastVisited) > 30*time.Minute { - delete(clientMap, key) - continue - } - } client := &tuic.Client{ TlsConfig: tlsConfig, QuicConfig: quicConfig, @@ -235,26 +222,60 @@ func NewTuic(option TuicOption) (*Tuic, error) { ReduceRtt: option.ReduceRtt, RequestTimeout: option.RequestTimeout, MaxUdpRelayPacketSize: option.MaxUdpRelayPacketSize, + Key: o, LastVisited: time.Now(), UDP: udp, } - clientMap[o] = client + clients.PushFront(client) runtime.SetFinalizer(client, closeTuicClient) return client } - removeClient := func(udp bool, opts ...dialer.Option) { - clientMap := tcpClientMap - clientMapMutex := tcpClientMapMutex + getClient := func(udp bool, opts ...dialer.Option) *tuic.Client { + clients := tcpClients + clientsMutex := tcpClientsMutex if udp { - clientMap = udpClientMap - clientMapMutex = udpClientMapMutex + clients = udpClients + clientsMutex = udpClientsMutex } - o := *dialer.ApplyOptions(opts...) + var o any = *dialer.ApplyOptions(opts...) + var bestClient *tuic.Client - clientMapMutex.Lock() - defer clientMapMutex.Unlock() - delete(clientMap, o) + func() { + clientsMutex.Lock() + defer clientsMutex.Unlock() + for it := clients.Front(); it != nil; { + client := it.Value + if client == nil { + next := it.Next() + clients.Remove(it) + it = next + continue + } + if client.Key == o { + if bestClient == nil { + bestClient = client + } else { + if client.OpenStreams.Load() < bestClient.OpenStreams.Load() { + bestClient = client + } + } + } + if time.Now().Sub(client.LastVisited) > 30*time.Minute { + next := it.Next() + clients.Remove(it) + it = next + continue + } + it = it.Next() + } + }() + + if bestClient == nil { + return newClient(udp, opts...) + } else { + return bestClient + } } return &Tuic{ @@ -266,8 +287,8 @@ func NewTuic(option TuicOption) (*Tuic, error) { iface: option.Interface, prefer: C.NewDNSPrefer(option.IPVersion), }, - getClient: getClient, - removeClient: removeClient, + newClient: newClient, + getClient: getClient, }, nil } diff --git a/transport/tuic/client.go b/transport/tuic/client.go index d5d2664d..332c664e 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -39,13 +39,14 @@ type Client struct { RequestTimeout int MaxUdpRelayPacketSize int + Key any LastVisited time.Time UDP bool quicConn quic.Connection connMutex sync.Mutex - openStreams atomic.Int32 + OpenStreams atomic.Int32 udpInputMap sync.Map } @@ -242,9 +243,9 @@ func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata, dialFn f if err != nil { return nil, err } - openStreams := t.openStreams.Add(1) + openStreams := t.OpenStreams.Add(1) if openStreams >= MaxOpenStreams { - t.openStreams.Add(-1) + t.OpenStreams.Add(-1) return nil, TooManyOpenStreams } stream, err := func() (stream *quicStreamConn, err error) { @@ -300,6 +301,9 @@ type quicStreamConn struct { lAddr net.Addr rAddr net.Addr client *Client + + closeOnce sync.Once + closeErr error } func (q *quicStreamConn) Write(p []byte) (n int, err error) { @@ -309,8 +313,15 @@ func (q *quicStreamConn) Write(p []byte) (n int, err error) { } func (q *quicStreamConn) Close() error { + q.closeOnce.Do(func() { + q.closeErr = q.close() + }) + return q.closeErr +} + +func (q *quicStreamConn) close() error { defer time.AfterFunc(C.DefaultTCPTimeout, func() { - q.client.openStreams.Add(-1) + q.client.OpenStreams.Add(-1) }) // https://github.com/cloudflare/cloudflared/commit/ed2bac026db46b239699ac5ce4fcf122d7cab2cd @@ -342,6 +353,11 @@ func (t *Client) ListenPacketContext(ctx context.Context, metadata *C.Metadata, if err != nil { return nil, err } + openStreams := t.OpenStreams.Add(1) + if openStreams >= MaxOpenStreams { + t.OpenStreams.Add(-1) + return nil, TooManyOpenStreams + } pipe1, pipe2 := net.Pipe() inputCh := make(chan udpData) @@ -380,16 +396,21 @@ type quicStreamPacketConn struct { closeOnce sync.Once closeErr error + closed bool } func (q *quicStreamPacketConn) Close() error { q.closeOnce.Do(func() { + q.closed = true q.closeErr = q.close() }) return q.closeErr } func (q *quicStreamPacketConn) close() (err error) { + defer time.AfterFunc(C.DefaultTCPTimeout, func() { + q.client.OpenStreams.Add(-1) + }) defer func() { q.client.deferQuicConn(q.quicConn, err) }() @@ -441,6 +462,9 @@ func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err erro if len(p) > q.client.MaxUdpRelayPacketSize { return 0, fmt.Errorf("udp packet too large(%d > %d)", len(p), q.client.MaxUdpRelayPacketSize) } + if q.closed { + return 0, net.ErrClosed + } defer func() { q.client.deferQuicConn(q.quicConn, err) }() From ed988dcdc55fb5aa0b76e9fdf4ded38dc5b422bc Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Fri, 25 Nov 2022 20:42:28 +0800 Subject: [PATCH 151/250] Chore: update dependencies --- go.mod | 10 +++++----- go.sum | 29 ++++++++++------------------- test/go.mod | 16 +++++++++------- test/go.sum | 38 +++++++++++++++++--------------------- 4 files changed, 41 insertions(+), 52 deletions(-) diff --git a/go.mod b/go.mod index 66eef6b1..52c0f472 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/gofrs/uuid v4.3.1+incompatible github.com/gorilla/websocket v1.5.0 github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c - github.com/mdlayher/netlink v1.6.2 + github.com/mdlayher/netlink v1.7.0 github.com/miekg/dns v1.1.50 github.com/oschwald/geoip2-golang v1.8.0 github.com/samber/lo v1.35.0 @@ -18,10 +18,10 @@ require ( go.etcd.io/bbolt v1.3.6 go.uber.org/atomic v1.10.0 go.uber.org/automaxprocs v1.5.1 - golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077 - golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0 + golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a + golang.org/x/net v0.2.1-0.20221117215542-ecf7fda6a59e golang.org/x/sync v0.1.0 - golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06 + golang.org/x/sys v0.2.1-0.20221110211117-d684c6f88669 gopkg.in/yaml.v3 v3.0.1 ) @@ -30,7 +30,7 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/josharian/native v1.0.0 // indirect - github.com/mdlayher/socket v0.2.3 // indirect + github.com/mdlayher/socket v0.4.0 // indirect github.com/oschwald/maxminddb-golang v1.10.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect diff --git a/go.sum b/go.sum index d92a0615..db742e0e 100644 --- a/go.sum +++ b/go.sum @@ -17,7 +17,6 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= 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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -39,12 +38,12 @@ github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= -github.com/mdlayher/netlink v1.6.2 h1:D2zGSkvYsJ6NreeED3JiVTu1lj2sIYATqSaZlhPzUgQ= -github.com/mdlayher/netlink v1.6.2/go.mod h1:O1HXX2sIWSMJ3Qn1BYZk1yZM+7iMki/uYGGiwGyq/iU= +github.com/mdlayher/netlink v1.7.0 h1:ZNGI4V7i1fJ94DPYtWhI/R85i/Q7ZxnuhUJQcJMoodI= +github.com/mdlayher/netlink v1.7.0/go.mod h1:nKO5CSjE/DJjVhk/TNp6vCE1ktVxEA8VEh8drhZzxsQ= github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= -github.com/mdlayher/socket v0.2.3 h1:XZA2X2TjdOwNoNPVPclRCURoX/hokBY8nkTmRZFEheM= -github.com/mdlayher/socket v0.2.3/go.mod h1:bz12/FozYNH/VbvC3q7TRIK/Y6dH1kCKsXaUeXi/FmY= +github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= +github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= @@ -83,8 +82,8 @@ go.uber.org/automaxprocs v1.5.1/go.mod h1:BF4eumQw0P9GtnuxxovUd06vwm1o18oMzFtK66 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.1.1-0.20221024173537-a3485e174077 h1:t5bjOfJPQfaG9NV1imLZM5E2uzaLGs5/NtyMtRNVjQ4= -golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a h1:diz9pEYuTIuLMJLs3rGDkeaTsNyRs6duYdFyPAxzE/U= +golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -93,7 +92,6 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -103,12 +101,10 @@ golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0 h1:vZ44Ys50wUISbPd+jC8cRLSvhyfX9Ii/ZmDnn/aiJtM= -golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.1-0.20221117215542-ecf7fda6a59e h1:IVOjWZQH/57UDcpX19vSmMz8w3ohroOMWohn8qWpRkg= +golang.org/x/net v0.2.1-0.20221117215542-ecf7fda6a59e/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -129,19 +125,14 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06 h1:E1pm64FqQa4v8dHd/bAneyMkR4hk8LTJhoSlc5mc1cM= -golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.1-0.20221110211117-d684c6f88669 h1:pvmSpBoSG0gD2LLPAX15QHPig8xsbU0tu1sSAmResqk= +golang.org/x/sys v0.2.1-0.20221110211117-d684c6f88669/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 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.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/test/go.mod b/test/go.mod index cad844ec..b9ec5387 100644 --- a/test/go.mod +++ b/test/go.mod @@ -3,12 +3,12 @@ module clash-test go 1.19 require ( - github.com/Dreamacro/clash v1.11.4 - github.com/docker/docker v20.10.17+incompatible + github.com/Dreamacro/clash v1.11.12 + github.com/docker/docker v20.10.21+incompatible github.com/docker/go-connections v0.4.0 github.com/miekg/dns v1.1.50 github.com/stretchr/testify v1.8.1 - golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0 + golang.org/x/net v0.2.1-0.20221117215542-ecf7fda6a59e ) replace github.com/Dreamacro/clash => ../ @@ -24,8 +24,8 @@ require ( github.com/gorilla/websocket v1.5.0 // indirect github.com/insomniacslk/dhcp v0.0.0-20221001123530-5308ebe5334c // indirect github.com/josharian/native v1.0.0 // indirect - github.com/mdlayher/netlink v1.6.2 // indirect - github.com/mdlayher/socket v0.2.3 // indirect + github.com/mdlayher/netlink v1.7.0 // indirect + github.com/mdlayher/socket v0.4.0 // indirect github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect @@ -34,14 +34,16 @@ require ( github.com/oschwald/maxminddb-golang v1.10.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/samber/lo v1.35.0 // indirect github.com/sirupsen/logrus v1.9.0 // indirect github.com/u-root/uio v0.0.0-20210528114334-82958018845c // indirect go.etcd.io/bbolt v1.3.6 // indirect go.uber.org/atomic v1.10.0 // indirect - golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077 // indirect + golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a // indirect + golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 // indirect golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06 // indirect + golang.org/x/sys v0.2.1-0.20221110211117-d684c6f88669 // indirect golang.org/x/text v0.4.0 // indirect golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect golang.org/x/tools v0.1.12 // indirect diff --git a/test/go.sum b/test/go.sum index d1ddf83f..69db4f48 100644 --- a/test/go.sum +++ b/test/go.sum @@ -8,8 +8,8 @@ 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/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v20.10.17+incompatible h1:JYCuMrWaVNophQTOrMMoSwudOVEfcegoZZrleKc1xwE= -github.com/docker/docker v20.10.17+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +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= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= @@ -25,7 +25,6 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= 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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= @@ -53,12 +52,12 @@ github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= -github.com/mdlayher/netlink v1.6.2 h1:D2zGSkvYsJ6NreeED3JiVTu1lj2sIYATqSaZlhPzUgQ= -github.com/mdlayher/netlink v1.6.2/go.mod h1:O1HXX2sIWSMJ3Qn1BYZk1yZM+7iMki/uYGGiwGyq/iU= +github.com/mdlayher/netlink v1.7.0 h1:ZNGI4V7i1fJ94DPYtWhI/R85i/Q7ZxnuhUJQcJMoodI= +github.com/mdlayher/netlink v1.7.0/go.mod h1:nKO5CSjE/DJjVhk/TNp6vCE1ktVxEA8VEh8drhZzxsQ= github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= -github.com/mdlayher/socket v0.2.3 h1:XZA2X2TjdOwNoNPVPclRCURoX/hokBY8nkTmRZFEheM= -github.com/mdlayher/socket v0.2.3/go.mod h1:bz12/FozYNH/VbvC3q7TRIK/Y6dH1kCKsXaUeXi/FmY= +github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= +github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= @@ -78,6 +77,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/samber/lo v1.35.0 h1:GlT8CV1GE+v97Y7MLF1wXvX6mjoxZ+hi61tj/ZcQwY0= +github.com/samber/lo v1.35.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= @@ -94,6 +95,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M= github.com/u-root/uio v0.0.0-20210528114334-82958018845c h1:BFvcl34IGnw8yvJi8hlqLFo9EshRInwWBs2M5fGWzQA= github.com/u-root/uio v0.0.0-20210528114334-82958018845c/go.mod h1:LpEX5FO/cB+WF4TYGY1V5qktpaZLkKkSegbr0V4eYXA= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -106,8 +108,10 @@ go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 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.1.1-0.20221024173537-a3485e174077 h1:t5bjOfJPQfaG9NV1imLZM5E2uzaLGs5/NtyMtRNVjQ4= -golang.org/x/crypto v0.1.1-0.20221024173537-a3485e174077/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a h1:diz9pEYuTIuLMJLs3rGDkeaTsNyRs6duYdFyPAxzE/U= +golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17 h1:3MTrJm4PyNL9NBqvYDSj3DHl46qQakyfqfWo4jgfaEM= +golang.org/x/exp v0.0.0-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE= 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.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -116,7 +120,6 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -128,14 +131,12 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0 h1:vZ44Ys50wUISbPd+jC8cRLSvhyfX9Ii/ZmDnn/aiJtM= -golang.org/x/net v0.1.1-0.20221102181756-a1278a7f7ee0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.1-0.20221117215542-ecf7fda6a59e h1:IVOjWZQH/57UDcpX19vSmMz8w3ohroOMWohn8qWpRkg= +golang.org/x/net v0.2.1-0.20221117215542-ecf7fda6a59e/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= 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.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220923202941-7f9b1623fab7/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -159,20 +160,15 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06 h1:E1pm64FqQa4v8dHd/bAneyMkR4hk8LTJhoSlc5mc1cM= -golang.org/x/sys v0.1.1-0.20221102194838-fc697a31fa06/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.1-0.20221110211117-d684c6f88669 h1:pvmSpBoSG0gD2LLPAX15QHPig8xsbU0tu1sSAmResqk= +golang.org/x/sys v0.2.1-0.20221110211117-d684c6f88669/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 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.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 h1:M73Iuj3xbbb9Uk1DYhzydthsj6oOd6l9bpuFcNoUvTs= From 6c204d2b77f4463048f671ecee3d80b9764b4361 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Fri, 25 Nov 2022 20:53:08 +0800 Subject: [PATCH 152/250] chroe: wrong variable --- adapter/outbound/util.go | 1 - 1 file changed, 1 deletion(-) diff --git a/adapter/outbound/util.go b/adapter/outbound/util.go index b322ba21..f32aae29 100644 --- a/adapter/outbound/util.go +++ b/adapter/outbound/util.go @@ -92,7 +92,6 @@ func resolveUDPAddrWithPrefer(ctx context.Context, network, address string, pref case C.IPv6Prefer: var ips []netip.Addr ips, err = resolver.LookupIPProxyServerHost(ctx, host) - var fallback netip.Addr if err == nil { for _, addr := range ips { if addr.Is6() { From 7f406459341619a5e3c8c8161ef82174932850bd Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 25 Nov 2022 22:42:07 +0800 Subject: [PATCH 153/250] chore: tuic use a udp pool too and auto close when outbound is garbage collected --- adapter/outbound/tuic.go | 99 ++++++++++++++++++++++++++-------------- transport/tuic/client.go | 1 + 2 files changed, 66 insertions(+), 34 deletions(-) diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index 30ea0485..a69b44ca 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -26,6 +26,7 @@ import ( type Tuic struct { *Base + dialFn func(ctx context.Context, t *Tuic, opts ...dialer.Option) (net.PacketConn, net.Addr, error) newClient func(udp bool, opts ...dialer.Option) *tuic.Client getClient func(udp bool, opts ...dialer.Option) *tuic.Client } @@ -59,15 +60,7 @@ type TuicOption struct { func (t *Tuic) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { opts = t.Base.DialOptions(opts...) dialFn := func(ctx context.Context) (net.PacketConn, net.Addr, error) { - pc, err := dialer.ListenPacket(ctx, "udp", "", opts...) - if err != nil { - return nil, nil, err - } - addr, err := resolveUDPAddrWithPrefer(ctx, "udp", t.addr, t.prefer) - if err != nil { - return nil, nil, err - } - return pc, addr, err + return t.dialFn(ctx, t, opts...) } conn, err := t.getClient(false, opts...).DialContext(ctx, metadata, dialFn) if errors.Is(err, tuic.TooManyOpenStreams) { @@ -83,15 +76,7 @@ func (t *Tuic) DialContext(ctx context.Context, metadata *C.Metadata, opts ...di func (t *Tuic) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { opts = t.Base.DialOptions(opts...) dialFn := func(ctx context.Context) (net.PacketConn, net.Addr, error) { - pc, err := dialer.ListenPacket(ctx, "udp", "", opts...) - if err != nil { - return nil, nil, err - } - addr, err := resolveUDPAddrWithPrefer(ctx, "udp", t.addr, t.prefer) - if err != nil { - return nil, nil, err - } - return pc, addr, err + return t.dialFn(ctx, t, opts...) } pc, err := t.getClient(true, opts...).ListenPacketContext(ctx, metadata, dialFn) if errors.Is(err, tuic.TooManyOpenStreams) { @@ -195,11 +180,67 @@ func NewTuic(option TuicOption) (*Tuic, error) { tlsConfig.ServerName = "" } tkn := tuic.GenTKN(option.Token) + + t := &Tuic{ + Base: &Base{ + name: option.Name, + addr: addr, + tp: C.Tuic, + udp: true, + iface: option.Interface, + prefer: C.NewDNSPrefer(option.IPVersion), + }, + } + + type dialResult struct { + pc net.PacketConn + addr net.Addr + err error + } + dialResultMap := make(map[any]dialResult) + dialResultMutex := &sync.Mutex{} tcpClients := list.New[*tuic.Client]() tcpClientsMutex := &sync.Mutex{} udpClients := list.New[*tuic.Client]() udpClientsMutex := &sync.Mutex{} - newClient := func(udp bool, opts ...dialer.Option) *tuic.Client { + t.dialFn = func(ctx context.Context, t *Tuic, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) { + var o any = *dialer.ApplyOptions(opts...) + + dialResultMutex.Lock() + dr, ok := dialResultMap[o] + dialResultMutex.Unlock() + if ok { + return dr.pc, dr.addr, dr.err + } + + pc, err = dialer.ListenPacket(ctx, "udp", "", opts...) + if err != nil { + return nil, nil, err + } + addr, err = resolveUDPAddrWithPrefer(ctx, "udp", t.addr, t.prefer) + if err != nil { + return nil, nil, err + } + + dr.pc, dr.addr, dr.err = pc, addr, err + + dialResultMutex.Lock() + dialResultMap[o] = dr + dialResultMutex.Unlock() + return pc, addr, err + } + closeFn := func(t *Tuic) { + dialResultMutex.Lock() + defer dialResultMutex.Unlock() + for key := range dialResultMap { + pc := dialResultMap[key].pc + if pc != nil { + _ = pc.Close() + } + delete(dialResultMap, key) + } + } + t.newClient = func(udp bool, opts ...dialer.Option) *tuic.Client { clients := tcpClients clientsMutex := tcpClientsMutex if udp { @@ -222,6 +263,7 @@ func NewTuic(option TuicOption) (*Tuic, error) { ReduceRtt: option.ReduceRtt, RequestTimeout: option.RequestTimeout, MaxUdpRelayPacketSize: option.MaxUdpRelayPacketSize, + Inference: t, Key: o, LastVisited: time.Now(), UDP: udp, @@ -230,7 +272,7 @@ func NewTuic(option TuicOption) (*Tuic, error) { runtime.SetFinalizer(client, closeTuicClient) return client } - getClient := func(udp bool, opts ...dialer.Option) *tuic.Client { + t.getClient = func(udp bool, opts ...dialer.Option) *tuic.Client { clients := tcpClients clientsMutex := tcpClientsMutex if udp { @@ -272,24 +314,13 @@ func NewTuic(option TuicOption) (*Tuic, error) { }() if bestClient == nil { - return newClient(udp, opts...) + return t.newClient(udp, opts...) } else { return bestClient } } - - return &Tuic{ - Base: &Base{ - name: option.Name, - addr: addr, - tp: C.Tuic, - udp: true, - iface: option.Interface, - prefer: C.NewDNSPrefer(option.IPVersion), - }, - newClient: newClient, - getClient: getClient, - }, nil + runtime.SetFinalizer(t, closeFn) + return t, nil } func closeTuicClient(client *tuic.Client) { diff --git a/transport/tuic/client.go b/transport/tuic/client.go index 332c664e..0ac03b5c 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -39,6 +39,7 @@ type Client struct { RequestTimeout int MaxUdpRelayPacketSize int + Inference any Key any LastVisited time.Time UDP bool From 90b40a8e5a81a6bf0d07985d6f037c7ed66721a9 Mon Sep 17 00:00:00 2001 From: Sizhe Sun Date: Sat, 26 Nov 2022 11:27:24 +0800 Subject: [PATCH 154/250] Fix: drop UDP packet which mismatched destination for VMess (#2410) Co-authored-by: SUN Sizhe --- adapter/outbound/vmess.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index 31d0a2cf..aaa9105d 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -20,6 +20,8 @@ import ( "golang.org/x/net/http2" ) +var ErrUDPRemoteAddrMismatch = errors.New("udp packet dropped due to mismatched remote address") + type Vmess struct { *Base client *vmess.Client @@ -358,7 +360,14 @@ type vmessPacketConn struct { rAddr net.Addr } +// WriteTo implments C.PacketConn.WriteTo +// Since VMess doesn't support full cone NAT by design, we verify if addr matches uc.rAddr, and drop the packet if not. func (uc *vmessPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) { + allowedAddr := uc.rAddr.(*net.UDPAddr) + destAddr := addr.(*net.UDPAddr) + if !(allowedAddr.IP.Equal(destAddr.IP) && allowedAddr.Port == destAddr.Port) { + return 0, ErrUDPRemoteAddrMismatch + } return uc.Conn.Write(b) } From a5ae2e891c4166fa304ac89ddf886cf6cf19559a Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sat, 26 Nov 2022 19:37:32 +0800 Subject: [PATCH 155/250] feat: support fast_open for hysteria, and unified parameter naming --- adapter/outbound/hysteria.go | 79 ++++++++++++++++++------------- transport/hysteria/core/client.go | 44 ++++++++++++----- 2 files changed, 79 insertions(+), 44 deletions(-) diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index 5a2850c6..5ba96a0a 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -10,6 +10,7 @@ import ( "fmt" "net" "os" + "reflect" "regexp" "strconv" "time" @@ -87,27 +88,33 @@ func (h *Hysteria) ListenPacketContext(ctx context.Context, metadata *C.Metadata type HysteriaOption struct { BasicOption - Name string `proxy:"name"` - Server string `proxy:"server"` - Port int `proxy:"port"` - Protocol string `proxy:"protocol,omitempty"` - ObfsProtocol string `proxy:"obfs-protocol,omitempty"` // compatible with Stash - Up string `proxy:"up"` - UpSpeed int `proxy:"up-speed,omitempty"` // compatible with Stash - Down string `proxy:"down"` - DownSpeed int `proxy:"down-speed,omitempty"` // compatible with Stash - Auth string `proxy:"auth,omitempty"` - AuthString string `proxy:"auth_str,omitempty"` - Obfs string `proxy:"obfs,omitempty"` - SNI string `proxy:"sni,omitempty"` - SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` - Fingerprint string `proxy:"fingerprint,omitempty"` - ALPN []string `proxy:"alpn,omitempty"` - CustomCA string `proxy:"ca,omitempty"` - CustomCAString string `proxy:"ca_str,omitempty"` - ReceiveWindowConn int `proxy:"recv_window_conn,omitempty"` - ReceiveWindow int `proxy:"recv_window,omitempty"` - DisableMTUDiscovery bool `proxy:"disable_mtu_discovery,omitempty"` + Name string `proxy:"name"` + Server string `proxy:"server"` + Port int `proxy:"port"` + Protocol string `proxy:"protocol,omitempty"` + ObfsProtocol string `proxy:"obfs-protocol,omitempty"` // compatible with Stash + Up string `proxy:"up"` + UpSpeed int `proxy:"up-speed,omitempty"` // compatible with Stash + Down string `proxy:"down"` + DownSpeed int `proxy:"down-speed,omitempty"` // compatible with Stash + Auth string `proxy:"auth,omitempty"` + OldAuthString string `proxy:"auth_str,omitempty"` + AuthString string `proxy:"auth-str,omitempty"` + Obfs string `proxy:"obfs,omitempty"` + SNI string `proxy:"sni,omitempty"` + SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + Fingerprint string `proxy:"fingerprint,omitempty"` + ALPN []string `proxy:"alpn,omitempty"` + CustomCA string `proxy:"ca,omitempty"` + OldCustomCAString string `proxy:"ca_str,omitempty"` + CustomCAString string `proxy:"ca-str,omitempty"` + OldReceiveWindowConn int `proxy:"recv_window_conn,omitempty"` + ReceiveWindowConn int `proxy:"recv-window-conn,omitempty"` + OldReceiveWindow int `proxy:"recv_window,omitempty"` + ReceiveWindow int `proxy:"recv-window,omitempty"` + OldDisableMTUDiscovery bool `proxy:"disable_mtu_discovery,omitempty"` + DisableMTUDiscovery bool `proxy:"disable-mtu-discovery,omitempty"` + FastOpen bool `proxy:"fast-open,omitempty"` } func (c *HysteriaOption) Speed() (uint64, uint64, error) { @@ -151,8 +158,8 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { if err != nil { return nil, fmt.Errorf("hysteria %s load ca error: %w", addr, err) } - } else if option.CustomCAString != "" { - bs = []byte(option.CustomCAString) + } else if compatibilityValue(option.CustomCAString, option.OldCustomCAString) != "" { + bs = []byte(compatibilityValue(option.CustomCAString, option.OldCustomCAString)) } if len(bs) > 0 { @@ -182,14 +189,13 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { } else { tlsConfig.NextProtos = []string{DefaultALPN} } - quicConfig := &quic.Config{ - InitialStreamReceiveWindow: uint64(option.ReceiveWindowConn), - MaxStreamReceiveWindow: uint64(option.ReceiveWindowConn), - InitialConnectionReceiveWindow: uint64(option.ReceiveWindow), - MaxConnectionReceiveWindow: uint64(option.ReceiveWindow), + InitialStreamReceiveWindow: uint64(compatibilityValue(option.ReceiveWindowConn, option.OldReceiveWindow)), + MaxStreamReceiveWindow: uint64(compatibilityValue(option.ReceiveWindowConn, option.OldReceiveWindow)), + InitialConnectionReceiveWindow: uint64(compatibilityValue(option.ReceiveWindow, option.OldReceiveWindow)), + MaxConnectionReceiveWindow: uint64(compatibilityValue(option.ReceiveWindow, option.OldReceiveWindow)), KeepAlivePeriod: 10 * time.Second, - DisablePathMTUDiscovery: option.DisableMTUDiscovery, + DisablePathMTUDiscovery: compatibilityValue(option.DisableMTUDiscovery, option.OldDisableMTUDiscovery), EnableDatagrams: true, } if option.ObfsProtocol != "" { @@ -198,11 +204,11 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { if option.Protocol == "" { option.Protocol = DefaultProtocol } - if option.ReceiveWindowConn == 0 { + if compatibilityValue( option.ReceiveWindowConn, option.OldReceiveWindowConn)== 0 { quicConfig.InitialStreamReceiveWindow = DefaultStreamReceiveWindow / 10 quicConfig.MaxStreamReceiveWindow = DefaultStreamReceiveWindow } - if option.ReceiveWindow == 0 { + if compatibilityValue( option.ReceiveWindow,option.OldReceiveWindow) == 0 { quicConfig.InitialConnectionReceiveWindow = DefaultConnectionReceiveWindow / 10 quicConfig.MaxConnectionReceiveWindow = DefaultConnectionReceiveWindow } @@ -210,7 +216,7 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { log.Infoln("hysteria: Path MTU Discovery is not yet supported on this platform") } - var auth = []byte(option.AuthString) + var auth = []byte(compatibilityValue(option.AuthString, option.OldAuthString)) if option.Auth != "" { auth, err = base64.StdEncoding.DecodeString(option.Auth) if err != nil { @@ -235,7 +241,7 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { client, err := core.NewClient( addr, option.Protocol, auth, tlsConfig, quicConfig, clientTransport, up, down, func(refBPS uint64) congestion.CongestionControl { return hyCongestion.NewBrutalSender(congestion.ByteCount(refBPS)) - }, obfuscator, + }, obfuscator, option.FastOpen, ) if err != nil { return nil, fmt.Errorf("hysteria %s create error: %w", addr, err) @@ -254,6 +260,13 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { }, nil } +func compatibilityValue[T any](prefer T, fallback T) T { + if reflect.ValueOf(prefer).IsZero() { + return fallback + } else { + return prefer + } +} func stringToBps(s string) uint64 { if s == "" { return 0 diff --git a/transport/hysteria/core/client.go b/transport/hysteria/core/client.go index 9c93f5a5..8a19570b 100644 --- a/transport/hysteria/core/client.go +++ b/transport/hysteria/core/client.go @@ -45,11 +45,12 @@ type Client struct { udpSessionMutex sync.RWMutex udpSessionMap map[uint32]chan *udpMessage udpDefragger defragger + fastOpen bool } func NewClient(serverAddr string, protocol string, auth []byte, tlsConfig *tls.Config, quicConfig *quic.Config, transport *transport.ClientTransport, sendBPS uint64, recvBPS uint64, congestionFactory CongestionFactory, - obfuscator obfs.Obfuscator) (*Client, error) { + obfuscator obfs.Obfuscator,fastOpen bool) (*Client, error) { quicConfig.DisablePathMTUDiscovery = quicConfig.DisablePathMTUDiscovery || pmtud_fix.DisablePathMTUDiscovery c := &Client{ transport: transport, @@ -62,6 +63,7 @@ func NewClient(serverAddr string, protocol string, auth []byte, tlsConfig *tls.C obfuscator: obfuscator, tlsConfig: tlsConfig, quicConfig: quicConfig, + fastOpen: fastOpen, } return c, nil } @@ -205,21 +207,27 @@ func (c *Client) DialTCP(addr string, dialer transport.PacketDialer) (net.Conn, _ = stream.Close() return nil, err } - // Read response - var sr serverResponse - err = struc.Unpack(stream, &sr) - if err != nil { - _ = stream.Close() - return nil, err - } - if !sr.OK { - _ = stream.Close() - return nil, fmt.Errorf("connection rejected: %s", sr.Message) + // If fast open is enabled, we return the stream immediately + // and defer the response handling to the first Read() call + if !c.fastOpen { + // Read response + var sr serverResponse + err = struc.Unpack(stream, &sr) + if err != nil { + _ = stream.Close() + return nil, err + } + if !sr.OK { + _ = stream.Close() + return nil, fmt.Errorf("connection rejected: %s", sr.Message) + } } + return &quicConn{ Orig: stream, PseudoLocalAddr: session.LocalAddr(), PseudoRemoteAddr: session.RemoteAddr(), + Established: !c.fastOpen, }, nil } @@ -288,9 +296,23 @@ type quicConn struct { Orig quic.Stream PseudoLocalAddr net.Addr PseudoRemoteAddr net.Addr + Established bool } func (w *quicConn) Read(b []byte) (n int, err error) { + if !w.Established { + var sr serverResponse + err := struc.Unpack(w.Orig, &sr) + if err != nil { + _ = w.Close() + return 0, err + } + if !sr.OK { + _ = w.Close() + return 0, fmt.Errorf("connection rejected: %s", sr.Message) + } + w.Established = true + } return w.Orig.Read(b) } From 516623cbbb025a5a8d22759ba2a434c5985fef61 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sat, 26 Nov 2022 19:48:16 +0800 Subject: [PATCH 156/250] chore: update demo --- docs/config.yaml | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/docs/config.yaml b/docs/config.yaml index d187a32d..1d93f636 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -432,21 +432,26 @@ proxies: type: hysteria server: server.com port: 443 - auth_str: yourpassword + auth_str: yourpassword # 将会在未来某个时候删除 + # auth-str: yourpassword # obfs: obfs_str # alpn: # - h3 protocol: udp # 支持 udp/wechat-video/faketcp up: "30 Mbps" # 若不写单位,默认为 Mbps down: "200 Mbps" # 若不写单位,默认为 Mbps - #sni: server.com - #skip-cert-verify: false - #recv_window_conn: 12582912 - #recv_window: 52428800 - #ca: "./my.ca" - #ca_str: "xyz" - #disable_mtu_discovery: false + # sni: server.com + # skip-cert-verify: false + # recv_window_conn: 12582912 # 将会在未来某个时候删除 + # recv-window-conn: 12582912 + # recv_window: 52428800 # 将会在未来某个时候删除 + # recv-window: 52428800 + # ca: "./my.ca" + # ca_str: "xyz" # 将会在未来某个时候删除 + # ca-str: "xyz" + # disable_mtu_discovery: false # fingerprint: xxxx + # fast-open: true # 支持 TCP 快速打开,默认为 false - name: "wg" type: wireguard From 8056b5573b6427403ad064334addfcc6fdbe085c Mon Sep 17 00:00:00 2001 From: Skimmle <117449739+Skimmle@users.noreply.github.com> Date: Sat, 26 Nov 2022 20:11:43 +0800 Subject: [PATCH 157/250] chore: update github action --- .github/workflows/prerelease.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index afd49cff..4a792eaa 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -1,5 +1,6 @@ name: Prerelease on: + workflow_dispatch: push: branches: - Alpha From ae76daf39392d3e0b37f3e0692bb8be1f8cac332 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 26 Nov 2022 21:14:56 +0800 Subject: [PATCH 158/250] chore: tuic add fast-open support --- adapter/outbound/tuic.go | 4 +++- transport/tuic/client.go | 42 +++++++++++++++++++++++++++++++++++----- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index a69b44ca..c2f4f917 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -47,6 +47,7 @@ type TuicOption struct { DisableSni bool `proxy:"disable_sni,omitempty"` MaxUdpRelayPacketSize int `proxy:"max_udp_relay_packet_size,omitempty"` + FastOpen bool `proxy:"fast-open,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` Fingerprint string `proxy:"fingerprint,omitempty"` CustomCA string `proxy:"ca,omitempty"` @@ -263,6 +264,7 @@ func NewTuic(option TuicOption) (*Tuic, error) { ReduceRtt: option.ReduceRtt, RequestTimeout: option.RequestTimeout, MaxUdpRelayPacketSize: option.MaxUdpRelayPacketSize, + FastOpen: option.FastOpen, Inference: t, Key: o, LastVisited: time.Now(), @@ -303,7 +305,7 @@ func NewTuic(option TuicOption) (*Tuic, error) { } } } - if time.Now().Sub(client.LastVisited) > 30*time.Minute { + if client.OpenStreams.Load() == 0 && time.Now().Sub(client.LastVisited) > 30*time.Minute { next := it.Next() clients.Remove(it) it = next diff --git a/transport/tuic/client.go b/transport/tuic/client.go index 0ac03b5c..95aeeac3 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -38,6 +38,7 @@ type Client struct { ReduceRtt bool RequestTimeout int MaxUdpRelayPacketSize int + FastOpen bool Inference any Key any @@ -282,18 +283,49 @@ func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata, dialFn f if t.RequestTimeout > 0 { _ = stream.SetReadDeadline(time.Now().Add(time.Duration(t.RequestTimeout) * time.Millisecond)) } - conn := N.NewBufferedConn(stream) + conn := &earlyConn{BufferedConn: N.NewBufferedConn(stream)} + if !t.FastOpen { + err = conn.Response() + if err != nil { + return nil, err + } + } + return conn, nil +} + +type earlyConn struct { + *N.BufferedConn + resOnce sync.Once + resErr error +} + +func (conn *earlyConn) response() error { response, err := ReadResponse(conn) if err != nil { _ = conn.Close() - return nil, err + return err } if response.IsFailed() { _ = conn.Close() - return nil, errors.New("connect failed") + return errors.New("connect failed") } - _ = stream.SetReadDeadline(time.Time{}) - return conn, nil + _ = 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.BufferedConn.Read(b) } type quicStreamConn struct { From 495fd191f212559e685f6fe64b4cc4c586318b63 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 26 Nov 2022 21:35:47 +0800 Subject: [PATCH 159/250] chore: clear config field name (be compatible with old field name) --- README.md | 15 +++---- adapter/outbound/hysteria.go | 78 +++++++++++++++--------------------- adapter/outbound/tuic.go | 22 +++++----- adapter/parser.go | 7 ++++ docs/config.yaml | 15 +++---- 5 files changed, 67 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index c0f39939..b62e212d 100644 --- a/README.md +++ b/README.md @@ -236,14 +236,15 @@ proxies: type: tuic token: TOKEN # ip: 127.0.0.1 # for overwriting the DNS lookup result of the server address set in option 'server' - # heartbeat_interval: 10000 + # heartbeat-interval: 10000 # alpn: [h3] - # disable_sni: true - reduce_rtt: true - # request_timeout: 8000 - udp_relay_mode: native # Available: "native", "quic". Default: "native" - # congestion_controller: bbr # Available: "cubic", "new_reno", "bbr". Default: "cubic" - # max_udp_relay_packet_size: 1500 + # disable-sni: true + reduce-rtt: true + # request-timeout: 8000 + udp-relay-mode: native # Available: "native", "quic". Default: "native" + # congestion-controller: bbr # Available: "cubic", "new_reno", "bbr". Default: "cubic" + # max-udp-relay-packet-size: 1500 + # fast-open: true # skip-cert-verify: true ``` diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index 5ba96a0a..aced8f8f 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -10,7 +10,6 @@ import ( "fmt" "net" "os" - "reflect" "regexp" "strconv" "time" @@ -88,33 +87,29 @@ func (h *Hysteria) ListenPacketContext(ctx context.Context, metadata *C.Metadata type HysteriaOption struct { BasicOption - Name string `proxy:"name"` - Server string `proxy:"server"` - Port int `proxy:"port"` - Protocol string `proxy:"protocol,omitempty"` - ObfsProtocol string `proxy:"obfs-protocol,omitempty"` // compatible with Stash - Up string `proxy:"up"` - UpSpeed int `proxy:"up-speed,omitempty"` // compatible with Stash - Down string `proxy:"down"` - DownSpeed int `proxy:"down-speed,omitempty"` // compatible with Stash - Auth string `proxy:"auth,omitempty"` - OldAuthString string `proxy:"auth_str,omitempty"` - AuthString string `proxy:"auth-str,omitempty"` - Obfs string `proxy:"obfs,omitempty"` - SNI string `proxy:"sni,omitempty"` - SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` - Fingerprint string `proxy:"fingerprint,omitempty"` - ALPN []string `proxy:"alpn,omitempty"` - CustomCA string `proxy:"ca,omitempty"` - OldCustomCAString string `proxy:"ca_str,omitempty"` - CustomCAString string `proxy:"ca-str,omitempty"` - OldReceiveWindowConn int `proxy:"recv_window_conn,omitempty"` - ReceiveWindowConn int `proxy:"recv-window-conn,omitempty"` - OldReceiveWindow int `proxy:"recv_window,omitempty"` - ReceiveWindow int `proxy:"recv-window,omitempty"` - OldDisableMTUDiscovery bool `proxy:"disable_mtu_discovery,omitempty"` - DisableMTUDiscovery bool `proxy:"disable-mtu-discovery,omitempty"` - FastOpen bool `proxy:"fast-open,omitempty"` + Name string `proxy:"name"` + Server string `proxy:"server"` + Port int `proxy:"port"` + Protocol string `proxy:"protocol,omitempty"` + ObfsProtocol string `proxy:"obfs-protocol,omitempty"` // compatible with Stash + Up string `proxy:"up"` + UpSpeed int `proxy:"up-speed,omitempty"` // compatible with Stash + Down string `proxy:"down"` + DownSpeed int `proxy:"down-speed,omitempty"` // compatible with Stash + Auth string `proxy:"auth,omitempty"` + OldAuthString string `proxy:"auth_str,omitempty"` + AuthString string `proxy:"auth-str,omitempty"` + Obfs string `proxy:"obfs,omitempty"` + SNI string `proxy:"sni,omitempty"` + SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` + Fingerprint string `proxy:"fingerprint,omitempty"` + ALPN []string `proxy:"alpn,omitempty"` + CustomCA string `proxy:"ca,omitempty"` + CustomCAString string `proxy:"ca-str,omitempty"` + ReceiveWindowConn int `proxy:"recv-window-conn,omitempty"` + ReceiveWindow int `proxy:"recv-window,omitempty"` + DisableMTUDiscovery bool `proxy:"disable-mtu-discovery,omitempty"` + FastOpen bool `proxy:"fast-open,omitempty"` } func (c *HysteriaOption) Speed() (uint64, uint64, error) { @@ -158,8 +153,8 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { if err != nil { return nil, fmt.Errorf("hysteria %s load ca error: %w", addr, err) } - } else if compatibilityValue(option.CustomCAString, option.OldCustomCAString) != "" { - bs = []byte(compatibilityValue(option.CustomCAString, option.OldCustomCAString)) + } else if option.CustomCAString != "" { + bs = []byte(option.CustomCAString) } if len(bs) > 0 { @@ -190,12 +185,12 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { tlsConfig.NextProtos = []string{DefaultALPN} } quicConfig := &quic.Config{ - InitialStreamReceiveWindow: uint64(compatibilityValue(option.ReceiveWindowConn, option.OldReceiveWindow)), - MaxStreamReceiveWindow: uint64(compatibilityValue(option.ReceiveWindowConn, option.OldReceiveWindow)), - InitialConnectionReceiveWindow: uint64(compatibilityValue(option.ReceiveWindow, option.OldReceiveWindow)), - MaxConnectionReceiveWindow: uint64(compatibilityValue(option.ReceiveWindow, option.OldReceiveWindow)), + InitialStreamReceiveWindow: uint64(option.ReceiveWindowConn), + MaxStreamReceiveWindow: uint64(option.ReceiveWindowConn), + InitialConnectionReceiveWindow: uint64(option.ReceiveWindow), + MaxConnectionReceiveWindow: uint64(option.ReceiveWindow), KeepAlivePeriod: 10 * time.Second, - DisablePathMTUDiscovery: compatibilityValue(option.DisableMTUDiscovery, option.OldDisableMTUDiscovery), + DisablePathMTUDiscovery: option.DisableMTUDiscovery, EnableDatagrams: true, } if option.ObfsProtocol != "" { @@ -204,11 +199,11 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { if option.Protocol == "" { option.Protocol = DefaultProtocol } - if compatibilityValue( option.ReceiveWindowConn, option.OldReceiveWindowConn)== 0 { + if option.ReceiveWindow == 0 { quicConfig.InitialStreamReceiveWindow = DefaultStreamReceiveWindow / 10 quicConfig.MaxStreamReceiveWindow = DefaultStreamReceiveWindow } - if compatibilityValue( option.ReceiveWindow,option.OldReceiveWindow) == 0 { + if option.ReceiveWindow == 0 { quicConfig.InitialConnectionReceiveWindow = DefaultConnectionReceiveWindow / 10 quicConfig.MaxConnectionReceiveWindow = DefaultConnectionReceiveWindow } @@ -216,7 +211,7 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { log.Infoln("hysteria: Path MTU Discovery is not yet supported on this platform") } - var auth = []byte(compatibilityValue(option.AuthString, option.OldAuthString)) + var auth = []byte(option.AuthString) if option.Auth != "" { auth, err = base64.StdEncoding.DecodeString(option.Auth) if err != nil { @@ -260,13 +255,6 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { }, nil } -func compatibilityValue[T any](prefer T, fallback T) T { - if reflect.ValueOf(prefer).IsZero() { - return fallback - } else { - return prefer - } -} func stringToBps(s string) uint64 { if s == "" { return 0 diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index c2f4f917..c480bede 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -38,23 +38,23 @@ type TuicOption struct { Port int `proxy:"port"` Token string `proxy:"token"` Ip string `proxy:"ip,omitempty"` - HeartbeatInterval int `proxy:"heartbeat_interval,omitempty"` + HeartbeatInterval int `proxy:"heartbeat-interval,omitempty"` ALPN []string `proxy:"alpn,omitempty"` - ReduceRtt bool `proxy:"reduce_rtt,omitempty"` - RequestTimeout int `proxy:"request_timeout,omitempty"` - UdpRelayMode string `proxy:"udp_relay_mode,omitempty"` - CongestionController string `proxy:"congestion_controller,omitempty"` - DisableSni bool `proxy:"disable_sni,omitempty"` - MaxUdpRelayPacketSize int `proxy:"max_udp_relay_packet_size,omitempty"` + ReduceRtt bool `proxy:"reduce-rtt,omitempty"` + RequestTimeout int `proxy:"request-timeout,omitempty"` + UdpRelayMode string `proxy:"udp-relay-mode,omitempty"` + CongestionController string `proxy:"congestion-controller,omitempty"` + DisableSni bool `proxy:"disable-sni,omitempty"` + MaxUdpRelayPacketSize int `proxy:"max-udp-relay-packet-size,omitempty"` FastOpen bool `proxy:"fast-open,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` Fingerprint string `proxy:"fingerprint,omitempty"` CustomCA string `proxy:"ca,omitempty"` - CustomCAString string `proxy:"ca_str,omitempty"` - ReceiveWindowConn int `proxy:"recv_window_conn,omitempty"` - ReceiveWindow int `proxy:"recv_window,omitempty"` - DisableMTUDiscovery bool `proxy:"disable_mtu_discovery,omitempty"` + CustomCAString string `proxy:"ca-str,omitempty"` + ReceiveWindowConn int `proxy:"recv-window-conn,omitempty"` + ReceiveWindow int `proxy:"recv-window,omitempty"` + DisableMTUDiscovery bool `proxy:"disable-mtu-discovery,omitempty"` } // DialContext implements C.ProxyAdapter diff --git a/adapter/parser.go b/adapter/parser.go index 0ce054f8..c0ed8792 100644 --- a/adapter/parser.go +++ b/adapter/parser.go @@ -2,6 +2,7 @@ package adapter import ( "fmt" + "strings" "github.com/Dreamacro/clash/adapter/outbound" "github.com/Dreamacro/clash/common/structure" @@ -9,6 +10,12 @@ import ( ) func ParseProxy(mapping map[string]any) (C.Proxy, error) { + newMapping := make(map[string]any) + for key := range mapping { + newMapping[strings.ReplaceAll(key, "_", "-")] = mapping[key] + } + mapping = newMapping + decoder := structure.NewDecoder(structure.Option{TagName: "proxy", WeaklyTypedInput: true}) proxyType, existType := mapping["type"].(string) if !existType { diff --git a/docs/config.yaml b/docs/config.yaml index 1d93f636..e7789dfb 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -469,14 +469,15 @@ proxies: type: tuic token: TOKEN # ip: 127.0.0.1 # for overwriting the DNS lookup result of the server address set in option 'server' - # heartbeat_interval: 10000 + # heartbeat-interval: 10000 # alpn: [h3] - # disable_sni: true - reduce_rtt: true - # request_timeout: 8000 - udp_relay_mode: native # Available: "native", "quic". Default: "native" - # congestion_controller: bbr # Available: "cubic", "new_reno", "bbr". Default: "cubic" - # max_udp_relay_packet_size: 1500 + # disable-sni: true + reduce-rtt: true + # request-timeout: 8000 + udp-relay-mode: native # Available: "native", "quic". Default: "native" + # congestion-controller: bbr # Available: "cubic", "new_reno", "bbr". Default: "cubic" + # max-udp-relay-packet-size: 1500 + # fast-open: true # skip-cert-verify: true # ShadowsocksR From 896d30b1512df4bd5d11db02a220def3205606ff Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 26 Nov 2022 23:53:59 +0800 Subject: [PATCH 160/250] chore: rebuild tuic client's code --- adapter/outbound/tuic.go | 180 ++++++---------------------------- transport/tuic/client.go | 60 ++++++++---- transport/tuic/pool_client.go | 177 +++++++++++++++++++++++++++++++++ transport/tuic/protocol.go | 42 +++++--- 4 files changed, 276 insertions(+), 183 deletions(-) create mode 100644 transport/tuic/pool_client.go diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index c480bede..58c79fa1 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -6,18 +6,14 @@ import ( "crypto/tls" "encoding/hex" "encoding/pem" - "errors" "fmt" "net" "os" - "runtime" "strconv" - "sync" "time" "github.com/metacubex/quic-go" - "github.com/Dreamacro/clash/common/generics/list" "github.com/Dreamacro/clash/component/dialer" tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" @@ -26,9 +22,7 @@ import ( type Tuic struct { *Base - dialFn func(ctx context.Context, t *Tuic, opts ...dialer.Option) (net.PacketConn, net.Addr, error) - newClient func(udp bool, opts ...dialer.Option) *tuic.Client - getClient func(udp bool, opts ...dialer.Option) *tuic.Client + client *tuic.PoolClient } type TuicOption struct { @@ -60,13 +54,7 @@ type TuicOption struct { // DialContext implements C.ProxyAdapter func (t *Tuic) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { opts = t.Base.DialOptions(opts...) - dialFn := func(ctx context.Context) (net.PacketConn, net.Addr, error) { - return t.dialFn(ctx, t, opts...) - } - conn, err := t.getClient(false, opts...).DialContext(ctx, metadata, dialFn) - if errors.Is(err, tuic.TooManyOpenStreams) { - conn, err = t.newClient(false, opts...).DialContext(ctx, metadata, dialFn) - } + conn, err := t.client.DialContext(ctx, metadata, opts...) if err != nil { return nil, err } @@ -76,19 +64,25 @@ func (t *Tuic) DialContext(ctx context.Context, metadata *C.Metadata, opts ...di // ListenPacketContext implements C.ProxyAdapter func (t *Tuic) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { opts = t.Base.DialOptions(opts...) - dialFn := func(ctx context.Context) (net.PacketConn, net.Addr, error) { - return t.dialFn(ctx, t, opts...) - } - pc, err := t.getClient(true, opts...).ListenPacketContext(ctx, metadata, dialFn) - if errors.Is(err, tuic.TooManyOpenStreams) { - pc, err = t.newClient(false, opts...).ListenPacketContext(ctx, metadata, dialFn) - } + pc, err := t.client.ListenPacketContext(ctx, metadata, opts...) if err != nil { return nil, err } return newPacketConn(pc, t), nil } +func (t *Tuic) dial(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) { + pc, err = dialer.ListenPacket(ctx, "udp", "", opts...) + if err != nil { + return nil, nil, err + } + addr, err = resolveUDPAddrWithPrefer(ctx, "udp", t.addr, t.prefer) + if err != nil { + return nil, nil, err + } + return +} + func NewTuic(option TuicOption) (*Tuic, error) { addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) serverName := option.Server @@ -192,139 +186,21 @@ func NewTuic(option TuicOption) (*Tuic, error) { prefer: C.NewDNSPrefer(option.IPVersion), }, } - - type dialResult struct { - pc net.PacketConn - addr net.Addr - err error + clientOption := &tuic.ClientOption{ + DialFn: t.dial, + TlsConfig: tlsConfig, + QuicConfig: quicConfig, + Host: host, + Token: tkn, + UdpRelayMode: option.UdpRelayMode, + CongestionController: option.CongestionController, + ReduceRtt: option.ReduceRtt, + RequestTimeout: option.RequestTimeout, + MaxUdpRelayPacketSize: option.MaxUdpRelayPacketSize, + FastOpen: option.FastOpen, } - dialResultMap := make(map[any]dialResult) - dialResultMutex := &sync.Mutex{} - tcpClients := list.New[*tuic.Client]() - tcpClientsMutex := &sync.Mutex{} - udpClients := list.New[*tuic.Client]() - udpClientsMutex := &sync.Mutex{} - t.dialFn = func(ctx context.Context, t *Tuic, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) { - var o any = *dialer.ApplyOptions(opts...) - dialResultMutex.Lock() - dr, ok := dialResultMap[o] - dialResultMutex.Unlock() - if ok { - return dr.pc, dr.addr, dr.err - } + t.client = tuic.NewClientPool(clientOption) - pc, err = dialer.ListenPacket(ctx, "udp", "", opts...) - if err != nil { - return nil, nil, err - } - addr, err = resolveUDPAddrWithPrefer(ctx, "udp", t.addr, t.prefer) - if err != nil { - return nil, nil, err - } - - dr.pc, dr.addr, dr.err = pc, addr, err - - dialResultMutex.Lock() - dialResultMap[o] = dr - dialResultMutex.Unlock() - return pc, addr, err - } - closeFn := func(t *Tuic) { - dialResultMutex.Lock() - defer dialResultMutex.Unlock() - for key := range dialResultMap { - pc := dialResultMap[key].pc - if pc != nil { - _ = pc.Close() - } - delete(dialResultMap, key) - } - } - t.newClient = func(udp bool, opts ...dialer.Option) *tuic.Client { - clients := tcpClients - clientsMutex := tcpClientsMutex - if udp { - clients = udpClients - clientsMutex = udpClientsMutex - } - - var o any = *dialer.ApplyOptions(opts...) - - clientsMutex.Lock() - defer clientsMutex.Unlock() - - client := &tuic.Client{ - TlsConfig: tlsConfig, - QuicConfig: quicConfig, - Host: host, - Token: tkn, - UdpRelayMode: option.UdpRelayMode, - CongestionController: option.CongestionController, - ReduceRtt: option.ReduceRtt, - RequestTimeout: option.RequestTimeout, - MaxUdpRelayPacketSize: option.MaxUdpRelayPacketSize, - FastOpen: option.FastOpen, - Inference: t, - Key: o, - LastVisited: time.Now(), - UDP: udp, - } - clients.PushFront(client) - runtime.SetFinalizer(client, closeTuicClient) - return client - } - t.getClient = func(udp bool, opts ...dialer.Option) *tuic.Client { - clients := tcpClients - clientsMutex := tcpClientsMutex - if udp { - clients = udpClients - clientsMutex = udpClientsMutex - } - - var o any = *dialer.ApplyOptions(opts...) - var bestClient *tuic.Client - - func() { - clientsMutex.Lock() - defer clientsMutex.Unlock() - for it := clients.Front(); it != nil; { - client := it.Value - if client == nil { - next := it.Next() - clients.Remove(it) - it = next - continue - } - if client.Key == o { - if bestClient == nil { - bestClient = client - } else { - if client.OpenStreams.Load() < bestClient.OpenStreams.Load() { - bestClient = client - } - } - } - if client.OpenStreams.Load() == 0 && time.Now().Sub(client.LastVisited) > 30*time.Minute { - next := it.Next() - clients.Remove(it) - it = next - continue - } - it = it.Next() - } - }() - - if bestClient == nil { - return t.newClient(udp, opts...) - } else { - return bestClient - } - } - runtime.SetFinalizer(t, closeFn) return t, nil } - -func closeTuicClient(client *tuic.Client) { - client.Close(tuic.ClientClosed) -} diff --git a/transport/tuic/client.go b/transport/tuic/client.go index 95aeeac3..f4eaf913 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -10,6 +10,7 @@ import ( "math/rand" "net" "net/netip" + "runtime" "sync" "sync/atomic" "time" @@ -17,6 +18,7 @@ import ( "github.com/metacubex/quic-go" N "github.com/Dreamacro/clash/common/net" + "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/transport/tuic/congestion" ) @@ -28,7 +30,9 @@ var ( const MaxOpenStreams = 100 - 90 -type Client struct { +type ClientOption struct { + DialFn func(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) + TlsConfig *tls.Config QuicConfig *quic.Config Host string @@ -39,27 +43,32 @@ type Client struct { RequestTimeout int MaxUdpRelayPacketSize int FastOpen bool +} - Inference any - Key any - LastVisited time.Time - UDP bool +type Client struct { + *ClientOption + udp bool quicConn quic.Connection connMutex sync.Mutex - OpenStreams atomic.Int32 + openStreams atomic.Int32 udpInputMap sync.Map + + // only ready for PoolClient + poolRef *PoolClient + optionRef any + lastVisited time.Time } -func (t *Client) getQuicConn(ctx context.Context, dialFn func(ctx context.Context) (net.PacketConn, net.Addr, error)) (quic.Connection, error) { +func (t *Client) getQuicConn(ctx context.Context) (quic.Connection, error) { t.connMutex.Lock() defer t.connMutex.Unlock() if t.quicConn != nil { return t.quicConn, nil } - pc, addr, err := dialFn(ctx) + pc, addr, err := t.DialFn(ctx) if err != nil { return nil, err } @@ -206,7 +215,7 @@ func (t *Client) getQuicConn(ctx context.Context, dialFn func(ctx context.Contex go sendAuthentication(quicConn) - if t.UDP { + if t.udp { go parseUDP(quicConn) } @@ -240,14 +249,14 @@ func (t *Client) Close(err error) { } } -func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata, dialFn func(ctx context.Context) (net.PacketConn, net.Addr, error)) (net.Conn, error) { - quicConn, err := t.getQuicConn(ctx, dialFn) +func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata) (net.Conn, error) { + quicConn, err := t.getQuicConn(ctx) if err != nil { return nil, err } - openStreams := t.OpenStreams.Add(1) + openStreams := t.openStreams.Add(1) if openStreams >= MaxOpenStreams { - t.OpenStreams.Add(-1) + t.openStreams.Add(-1) return nil, TooManyOpenStreams } stream, err := func() (stream *quicStreamConn, err error) { @@ -354,7 +363,7 @@ func (q *quicStreamConn) Close() error { func (q *quicStreamConn) close() error { defer time.AfterFunc(C.DefaultTCPTimeout, func() { - q.client.OpenStreams.Add(-1) + q.client.openStreams.Add(-1) }) // https://github.com/cloudflare/cloudflared/commit/ed2bac026db46b239699ac5ce4fcf122d7cab2cd @@ -381,14 +390,14 @@ func (q *quicStreamConn) RemoteAddr() net.Addr { var _ net.Conn = &quicStreamConn{} -func (t *Client) ListenPacketContext(ctx context.Context, metadata *C.Metadata, dialFn func(ctx context.Context) (net.PacketConn, net.Addr, error)) (net.PacketConn, error) { - quicConn, err := t.getQuicConn(ctx, dialFn) +func (t *Client) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (net.PacketConn, error) { + quicConn, err := t.getQuicConn(ctx) if err != nil { return nil, err } - openStreams := t.OpenStreams.Add(1) + openStreams := t.openStreams.Add(1) if openStreams >= MaxOpenStreams { - t.OpenStreams.Add(-1) + t.openStreams.Add(-1) return nil, TooManyOpenStreams } @@ -442,7 +451,7 @@ func (q *quicStreamPacketConn) Close() error { func (q *quicStreamPacketConn) close() (err error) { defer time.AfterFunc(C.DefaultTCPTimeout, func() { - q.client.OpenStreams.Add(-1) + q.client.openStreams.Add(-1) }) defer func() { q.client.deferQuicConn(q.quicConn, err) @@ -539,3 +548,16 @@ func (q *quicStreamPacketConn) LocalAddr() net.Addr { } var _ net.PacketConn = &quicStreamPacketConn{} + +func NewClient(clientOption *ClientOption, udp bool) *Client { + c := &Client{ + ClientOption: clientOption, + udp: udp, + } + runtime.SetFinalizer(c, closeClient) + return c +} + +func closeClient(client *Client) { + client.Close(ClientClosed) +} diff --git a/transport/tuic/pool_client.go b/transport/tuic/pool_client.go new file mode 100644 index 00000000..19ebc4eb --- /dev/null +++ b/transport/tuic/pool_client.go @@ -0,0 +1,177 @@ +package tuic + +import ( + "context" + "errors" + "net" + "runtime" + "sync" + "time" + + "github.com/Dreamacro/clash/common/generics/list" + "github.com/Dreamacro/clash/component/dialer" + C "github.com/Dreamacro/clash/constant" +) + +type dialResult struct { + pc net.PacketConn + addr net.Addr + err error +} + +type PoolClient struct { + *ClientOption + + dialResultMap map[any]dialResult + dialResultMutex *sync.Mutex + tcpClients *list.List[*Client] + tcpClientsMutex *sync.Mutex + udpClients *list.List[*Client] + udpClientsMutex *sync.Mutex +} + +func (t *PoolClient) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (net.Conn, error) { + conn, err := t.getClient(false, opts...).DialContext(ctx, metadata) + if errors.Is(err, TooManyOpenStreams) { + conn, err = t.newClient(false, opts...).DialContext(ctx, metadata) + } + if err != nil { + return nil, err + } + return conn, err +} + +func (t *PoolClient) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (net.PacketConn, error) { + pc, err := t.getClient(true, opts...).ListenPacketContext(ctx, metadata) + if errors.Is(err, TooManyOpenStreams) { + pc, err = t.newClient(false, opts...).ListenPacketContext(ctx, metadata) + } + if err != nil { + return nil, err + } + return pc, nil +} + +func (t *PoolClient) dial(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) { + var o any = *dialer.ApplyOptions(opts...) + + t.dialResultMutex.Lock() + dr, ok := t.dialResultMap[o] + t.dialResultMutex.Unlock() + if ok { + return dr.pc, dr.addr, dr.err + } + + pc, addr, err = t.DialFn(ctx, opts...) + if err != nil { + return nil, nil, err + } + + dr.pc, dr.addr, dr.err = pc, addr, err + + t.dialResultMutex.Lock() + t.dialResultMap[o] = dr + t.dialResultMutex.Unlock() + return pc, addr, err +} + +func (t *PoolClient) Close() { + t.dialResultMutex.Lock() + defer t.dialResultMutex.Unlock() + for key := range t.dialResultMap { + pc := t.dialResultMap[key].pc + if pc != nil { + _ = pc.Close() + } + delete(t.dialResultMap, key) + } +} + +func (t *PoolClient) newClient(udp bool, opts ...dialer.Option) *Client { + clients := t.tcpClients + clientsMutex := t.tcpClientsMutex + if udp { + clients = t.udpClients + clientsMutex = t.udpClientsMutex + } + + var o any = *dialer.ApplyOptions(opts...) + + clientsMutex.Lock() + defer clientsMutex.Unlock() + + client := NewClient(t.ClientOption, udp) + client.poolRef = t // make sure pool has a reference + client.optionRef = o + client.lastVisited = time.Now() + + clients.PushFront(client) + return client +} + +func (t *PoolClient) getClient(udp bool, opts ...dialer.Option) *Client { + clients := t.tcpClients + clientsMutex := t.tcpClientsMutex + if udp { + clients = t.udpClients + clientsMutex = t.udpClientsMutex + } + + var o any = *dialer.ApplyOptions(opts...) + var bestClient *Client + + func() { + clientsMutex.Lock() + defer clientsMutex.Unlock() + for it := clients.Front(); it != nil; { + client := it.Value + if client == nil { + next := it.Next() + clients.Remove(it) + it = next + continue + } + if client.optionRef == o { + if bestClient == nil { + bestClient = client + } else { + if client.openStreams.Load() < bestClient.openStreams.Load() { + bestClient = client + } + } + } + if client.openStreams.Load() == 0 && time.Now().Sub(client.lastVisited) > 30*time.Minute { + next := it.Next() + clients.Remove(it) + it = next + continue + } + it = it.Next() + } + }() + + if bestClient == nil { + return t.newClient(udp, opts...) + } else { + bestClient.lastVisited = time.Now() + return bestClient + } +} + +func NewClientPool(clientOption *ClientOption) *PoolClient { + p := &PoolClient{ + ClientOption: clientOption, + dialResultMap: make(map[any]dialResult), + dialResultMutex: &sync.Mutex{}, + tcpClients: list.New[*Client](), + tcpClientsMutex: &sync.Mutex{}, + udpClients: list.New[*Client](), + udpClientsMutex: &sync.Mutex{}, + } + runtime.SetFinalizer(p, closeClientPool) + return p +} + +func closeClientPool(client *PoolClient) { + client.Close() +} diff --git a/transport/tuic/protocol.go b/transport/tuic/protocol.go index 913f0d51..98f7cd96 100644 --- a/transport/tuic/protocol.go +++ b/transport/tuic/protocol.go @@ -178,8 +178,8 @@ func NewPacket(ASSOC_ID uint32, LEN uint16, ADDR Address, DATA []byte) Packet { } } -func ReadPacket(reader BufferedReader) (c Packet, err error) { - c.CommandHead, err = ReadCommandHead(reader) +func ReadPacketWithHead(head CommandHead, reader BufferedReader) (c Packet, err error) { + c.CommandHead = head if err != nil { return } @@ -206,6 +206,14 @@ func ReadPacket(reader BufferedReader) (c Packet, err error) { return } +func ReadPacket(reader BufferedReader) (c Packet, err error) { + head, err := ReadCommandHead(reader) + if err != nil { + return + } + return ReadPacketWithHead(head, reader) +} + func (c Packet) WriteTo(writer BufferedWriter) (err error) { err = c.CommandHead.WriteTo(writer) if err != nil { @@ -272,17 +280,22 @@ func NewHeartbeat() Heartbeat { } } -func ReadHeartbeat(reader BufferedReader) (c Response, err error) { - c.CommandHead, err = ReadCommandHead(reader) - if err != nil { - return - } +func ReadHeartbeatWithHead(head CommandHead, reader BufferedReader) (c Response, err error) { + c.CommandHead = head if c.CommandHead.TYPE != HeartbeatType { err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE) } return } +func ReadHeartbeat(reader BufferedReader) (c Response, err error) { + head, err := ReadCommandHead(reader) + if err != nil { + return + } + return ReadHeartbeatWithHead(head, reader) +} + type Response struct { CommandHead REP byte @@ -295,11 +308,8 @@ func NewResponse(REP byte) Response { } } -func ReadResponse(reader BufferedReader) (c Response, err error) { - c.CommandHead, err = ReadCommandHead(reader) - if err != nil { - return - } +func ReadResponseWithHead(head CommandHead, reader BufferedReader) (c Response, err error) { + c.CommandHead = head if c.CommandHead.TYPE != ResponseType { err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE) } @@ -310,6 +320,14 @@ func ReadResponse(reader BufferedReader) (c Response, err error) { return } +func ReadResponse(reader BufferedReader) (c Response, err error) { + head, err := ReadCommandHead(reader) + if err != nil { + return + } + return ReadResponseWithHead(head, reader) +} + func (c Response) WriteTo(writer BufferedWriter) (err error) { err = c.CommandHead.WriteTo(writer) if err != nil { From 423850a7aacba37b77ecb831f6342f0f0b9b64eb Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 27 Nov 2022 09:38:20 +0800 Subject: [PATCH 161/250] chore: allow to set max-open-streams for tuic --- adapter/outbound/tuic.go | 18 ++++++++++++++++++ transport/tuic/client.go | 9 ++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index 58c79fa1..10fbfd2e 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -7,6 +7,7 @@ import ( "encoding/hex" "encoding/pem" "fmt" + "math" "net" "os" "strconv" @@ -42,6 +43,7 @@ type TuicOption struct { MaxUdpRelayPacketSize int `proxy:"max-udp-relay-packet-size,omitempty"` FastOpen bool `proxy:"fast-open,omitempty"` + MaxOpenStreams int `proxy:"max-open-streams,omitempty"` SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"` Fingerprint string `proxy:"fingerprint,omitempty"` CustomCA string `proxy:"ca,omitempty"` @@ -148,11 +150,20 @@ func NewTuic(option TuicOption) (*Tuic, error) { option.MaxUdpRelayPacketSize = 1500 } + if option.MaxOpenStreams == 0 { + option.MaxOpenStreams = 100 + } + + // ensure server's incoming stream can handle correctly, increase to 1.1x + quicMaxOpenStreams := int64(option.MaxOpenStreams) + quicMaxOpenStreams = quicMaxOpenStreams + int64(math.Ceil(float64(quicMaxOpenStreams)/10.0)) quicConfig := &quic.Config{ InitialStreamReceiveWindow: uint64(option.ReceiveWindowConn), MaxStreamReceiveWindow: uint64(option.ReceiveWindowConn), InitialConnectionReceiveWindow: uint64(option.ReceiveWindow), MaxConnectionReceiveWindow: uint64(option.ReceiveWindow), + MaxIncomingStreams: quicMaxOpenStreams, + MaxIncomingUniStreams: quicMaxOpenStreams, KeepAlivePeriod: time.Duration(option.HeartbeatInterval) * time.Millisecond, DisablePathMTUDiscovery: option.DisableMTUDiscovery, EnableDatagrams: true, @@ -186,6 +197,12 @@ func NewTuic(option TuicOption) (*Tuic, error) { prefer: C.NewDNSPrefer(option.IPVersion), }, } + // to avoid tuic's "too many open streams", decrease to 0.9x + clientMaxOpenStreams := int64(option.MaxOpenStreams) + clientMaxOpenStreams = clientMaxOpenStreams - int64(math.Ceil(float64(clientMaxOpenStreams)/10.0)) + if clientMaxOpenStreams < 1 { + clientMaxOpenStreams = 1 + } clientOption := &tuic.ClientOption{ DialFn: t.dial, TlsConfig: tlsConfig, @@ -198,6 +215,7 @@ func NewTuic(option TuicOption) (*Tuic, error) { RequestTimeout: option.RequestTimeout, MaxUdpRelayPacketSize: option.MaxUdpRelayPacketSize, FastOpen: option.FastOpen, + MaxOpenStreams: clientMaxOpenStreams, } t.client = tuic.NewClientPool(clientOption) diff --git a/transport/tuic/client.go b/transport/tuic/client.go index f4eaf913..ca0e78a1 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -28,8 +28,6 @@ var ( TooManyOpenStreams = errors.New("tuic: too many open streams") ) -const MaxOpenStreams = 100 - 90 - type ClientOption struct { DialFn func(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) @@ -43,6 +41,7 @@ type ClientOption struct { RequestTimeout int MaxUdpRelayPacketSize int FastOpen bool + MaxOpenStreams int64 } type Client struct { @@ -52,7 +51,7 @@ type Client struct { quicConn quic.Connection connMutex sync.Mutex - openStreams atomic.Int32 + openStreams atomic.Int64 udpInputMap sync.Map @@ -255,7 +254,7 @@ func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata) (net.Con return nil, err } openStreams := t.openStreams.Add(1) - if openStreams >= MaxOpenStreams { + if openStreams >= t.MaxOpenStreams { t.openStreams.Add(-1) return nil, TooManyOpenStreams } @@ -396,7 +395,7 @@ func (t *Client) ListenPacketContext(ctx context.Context, metadata *C.Metadata) return nil, err } openStreams := t.openStreams.Add(1) - if openStreams >= MaxOpenStreams { + if openStreams >= t.MaxOpenStreams { t.openStreams.Add(-1) return nil, TooManyOpenStreams } From 0647cee02a7ae1ec9e6675f4c1b8b522971435ed Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 27 Nov 2022 10:37:10 +0800 Subject: [PATCH 162/250] chore: update quic-go --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 06fa6eb8..a20ada5c 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/jpillora/backoff v1.0.0 github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/mdlayher/netlink v1.1.1 - github.com/metacubex/quic-go v0.31.1-0.20221125020617-0f0618ad3eaa + github.com/metacubex/quic-go v0.31.1-0.20221127023445-9f0ce65a734e github.com/metacubex/sing-wireguard v0.0.0-20221109114053-16c22adda03c github.com/miekg/dns v1.1.50 github.com/oschwald/geoip2-golang v1.8.0 diff --git a/go.sum b/go.sum index 73287249..46c1c049 100644 --- a/go.sum +++ b/go.sum @@ -89,8 +89,8 @@ github.com/mdlayher/netlink v1.1.1 h1:VqG+Voq9V4uZ+04vjIrcSCWDpf91B1xxbP4QBUmUJE github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o= github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= -github.com/metacubex/quic-go v0.31.1-0.20221125020617-0f0618ad3eaa h1:Zc0nF/kgKDRn/Ab9jL1KNjOOe8JaM/sjsak1gNVVG6g= -github.com/metacubex/quic-go v0.31.1-0.20221125020617-0f0618ad3eaa/go.mod h1:7NPWVTLiX2Ss9q9gBNZaNHsPqZ3Tg/ApyrXxxUYbl78= +github.com/metacubex/quic-go v0.31.1-0.20221127023445-9f0ce65a734e h1:RnfC6+sShJ3biU2Q2wuh4FxZ8/3fp1QG+1zAfswVehA= +github.com/metacubex/quic-go v0.31.1-0.20221127023445-9f0ce65a734e/go.mod h1:7NPWVTLiX2Ss9q9gBNZaNHsPqZ3Tg/ApyrXxxUYbl78= github.com/metacubex/sing-wireguard v0.0.0-20221109114053-16c22adda03c h1:VHtXDny/TNOF7YDT9d9Qkr+x6K1O4cejXLlyPUXDeXQ= github.com/metacubex/sing-wireguard v0.0.0-20221109114053-16c22adda03c/go.mod h1:fULJ451x1/XlpIhl+Oo+EPGKla9tFZaqT5dKLrZ+NvM= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= From 17c081a40ca283fe0826852309eb4df08f3ab2b4 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 27 Nov 2022 11:09:56 +0800 Subject: [PATCH 163/250] add support for hysteria udp port hopping (#269) * add support for hysteria udp port hopping * add ports field for hysteria * change method for udp connection Co-authored-by: geoleonsh --- adapter/outbound/hysteria.go | 20 +- transport/hysteria/conns/udp/hop.go | 350 +++++++++++++++++++++++++ transport/hysteria/core/client.go | 34 +-- transport/hysteria/transport/client.go | 36 ++- transport/hysteria/utils/misc.go | 7 + 5 files changed, 416 insertions(+), 31 deletions(-) create mode 100644 transport/hysteria/conns/udp/hop.go diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index aced8f8f..fd441dc1 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -37,8 +37,9 @@ const ( DefaultConnectionReceiveWindow = 67108864 // 64 MB/s DefaultMaxIncomingStreams = 1024 - DefaultALPN = "hysteria" - DefaultProtocol = "udp" + DefaultALPN = "hysteria" + DefaultProtocol = "udp" + DefaultHopInterval = 10 ) var rateStringRegexp = regexp.MustCompile(`^(\d+)\s*([KMGT]?)([Bb])ps$`) @@ -90,6 +91,7 @@ type HysteriaOption struct { Name string `proxy:"name"` Server string `proxy:"server"` Port int `proxy:"port"` + Ports string `proxy:"ports"` Protocol string `proxy:"protocol,omitempty"` ObfsProtocol string `proxy:"obfs-protocol,omitempty"` // compatible with Stash Up string `proxy:"up"` @@ -110,6 +112,7 @@ type HysteriaOption struct { ReceiveWindow int `proxy:"recv-window,omitempty"` DisableMTUDiscovery bool `proxy:"disable-mtu-discovery,omitempty"` FastOpen bool `proxy:"fast-open,omitempty"` + HopInterval int `proxy:"hop-interval"` } func (c *HysteriaOption) Speed() (uint64, uint64, error) { @@ -133,8 +136,13 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { Timeout: 8 * time.Second, }, } + var addr string + if option.Ports == "" { + addr = net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) + } else { + addr = net.JoinHostPort(option.Server, option.Ports) + } - addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) serverName := option.Server if option.SNI != "" { serverName = option.SNI @@ -199,6 +207,10 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { if option.Protocol == "" { option.Protocol = DefaultProtocol } + if option.HopInterval == 0 { + option.HopInterval = DefaultHopInterval + } + hopInterval := time.Duration(int64(option.HopInterval)) * time.Second if option.ReceiveWindow == 0 { quicConfig.InitialStreamReceiveWindow = DefaultStreamReceiveWindow / 10 quicConfig.MaxStreamReceiveWindow = DefaultStreamReceiveWindow @@ -236,7 +248,7 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { client, err := core.NewClient( addr, option.Protocol, auth, tlsConfig, quicConfig, clientTransport, up, down, func(refBPS uint64) congestion.CongestionControl { return hyCongestion.NewBrutalSender(congestion.ByteCount(refBPS)) - }, obfuscator, option.FastOpen, + }, obfuscator, hopInterval, option.FastOpen, ) if err != nil { return nil, fmt.Errorf("hysteria %s create error: %w", addr, err) diff --git a/transport/hysteria/conns/udp/hop.go b/transport/hysteria/conns/udp/hop.go new file mode 100644 index 00000000..e4958821 --- /dev/null +++ b/transport/hysteria/conns/udp/hop.go @@ -0,0 +1,350 @@ +package udp + +import ( + "errors" + "math/rand" + "net" + "strconv" + "strings" + "sync" + "syscall" + "time" + + "github.com/Dreamacro/clash/transport/hysteria/obfs" + "github.com/Dreamacro/clash/transport/hysteria/utils" +) + +const ( + packetQueueSize = 1024 +) + +// ObfsUDPHopClientPacketConn is the UDP port-hopping packet connection for client side. +// It hops to a different local & server port every once in a while. +type ObfsUDPHopClientPacketConn struct { + serverAddr net.Addr // Combined udpHopAddr + serverAddrs []net.Addr + hopInterval time.Duration + + obfs obfs.Obfuscator + + connMutex sync.RWMutex + prevConn net.PacketConn + currentConn net.PacketConn + addrIndex int + + readBufferSize int + writeBufferSize int + + recvQueue chan *udpPacket + closeChan chan struct{} + closed bool + + bufPool sync.Pool +} + +type udpHopAddr string + +func (a *udpHopAddr) Network() string { + return "udp-hop" +} + +func (a *udpHopAddr) String() string { + return string(*a) +} + +type udpPacket struct { + buf []byte + n int + addr net.Addr +} + +func NewObfsUDPHopClientPacketConn(server string, hopInterval time.Duration, obfs obfs.Obfuscator, dialer utils.PacketDialer) (*ObfsUDPHopClientPacketConn, error) { + host, ports, err := parseAddr(server) + if err != nil { + return nil, err + } + // Resolve the server IP address, then attach the ports to UDP addresses + ip, err := dialer.RemoteAddr(host) + if err != nil { + return nil, err + } + serverAddrs := make([]net.Addr, len(ports)) + for i, port := range ports { + serverAddrs[i] = &net.UDPAddr{ + IP: net.ParseIP(ip.String()), + Port: int(port), + } + } + hopAddr := udpHopAddr(server) + conn := &ObfsUDPHopClientPacketConn{ + serverAddr: &hopAddr, + serverAddrs: serverAddrs, + hopInterval: hopInterval, + obfs: obfs, + addrIndex: rand.Intn(len(serverAddrs)), + recvQueue: make(chan *udpPacket, packetQueueSize), + closeChan: make(chan struct{}), + bufPool: sync.Pool{ + New: func() interface{} { + return make([]byte, udpBufferSize) + }, + }, + } + curConn, err := dialer.ListenPacket() + if err != nil { + return nil, err + } + if obfs != nil { + conn.currentConn = NewObfsUDPConn(curConn, obfs) + } else { + conn.currentConn = curConn + } + go conn.recvRoutine(conn.currentConn) + go conn.hopRoutine(dialer) + return conn, nil +} + +func (c *ObfsUDPHopClientPacketConn) recvRoutine(conn net.PacketConn) { + for { + buf := c.bufPool.Get().([]byte) + n, addr, err := conn.ReadFrom(buf) + if err != nil { + return + } + select { + case c.recvQueue <- &udpPacket{buf, n, addr}: + default: + // Drop the packet if the queue is full + c.bufPool.Put(buf) + } + } +} + +func (c *ObfsUDPHopClientPacketConn) hopRoutine(dialer utils.PacketDialer) { + ticker := time.NewTicker(c.hopInterval) + defer ticker.Stop() + for { + select { + case <-ticker.C: + c.hop(dialer) + case <-c.closeChan: + return + } + } +} + +func (c *ObfsUDPHopClientPacketConn) hop(dialer utils.PacketDialer) { + c.connMutex.Lock() + defer c.connMutex.Unlock() + if c.closed { + return + } + newConn, err := dialer.ListenPacket() + if err != nil { + // Skip this hop if failed to listen + return + } + // Close prevConn, + // prevConn <- currentConn + // currentConn <- newConn + // update addrIndex + // + // We need to keep receiving packets from the previous connection, + // because otherwise there will be packet loss due to the time gap + // between we hop to a new port and the server acknowledges this change. + if c.prevConn != nil { + _ = c.prevConn.Close() // recvRoutine will exit on error + } + c.prevConn = c.currentConn + if c.obfs != nil { + c.currentConn = NewObfsUDPConn(newConn, c.obfs) + } else { + c.currentConn = newConn + } + // Set buffer sizes if previously set + if c.readBufferSize > 0 { + _ = trySetPacketConnReadBuffer(c.currentConn, c.readBufferSize) + } + if c.writeBufferSize > 0 { + _ = trySetPacketConnWriteBuffer(c.currentConn, c.writeBufferSize) + } + go c.recvRoutine(c.currentConn) + c.addrIndex = rand.Intn(len(c.serverAddrs)) +} + +func (c *ObfsUDPHopClientPacketConn) ReadFrom(b []byte) (int, net.Addr, error) { + for { + select { + case p := <-c.recvQueue: + /* + // Check if the packet is from one of the server addresses + for _, addr := range c.serverAddrs { + if addr.String() == p.addr.String() { + // Copy the packet to the buffer + n := copy(b, p.buf[:p.n]) + c.bufPool.Put(p.buf) + return n, c.serverAddr, nil + } + } + // Drop the packet, continue + c.bufPool.Put(p.buf) + */ + // The above code was causing performance issues when the range is large, + // so we skip the check for now. Should probably still check by using a map + // or something in the future. + n := copy(b, p.buf[:p.n]) + c.bufPool.Put(p.buf) + return n, c.serverAddr, nil + case <-c.closeChan: + return 0, nil, net.ErrClosed + } + // Ignore packets from other addresses + } +} + +func (c *ObfsUDPHopClientPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) { + c.connMutex.RLock() + defer c.connMutex.RUnlock() + /* + // Check if the address is the server address + if addr.String() != c.serverAddr.String() { + return 0, net.ErrWriteToConnected + } + */ + // Skip the check for now, always write to the server + return c.currentConn.WriteTo(b, c.serverAddrs[c.addrIndex]) +} + +func (c *ObfsUDPHopClientPacketConn) Close() error { + c.connMutex.Lock() + defer c.connMutex.Unlock() + if c.closed { + return nil + } + // Close prevConn and currentConn + // Close closeChan to unblock ReadFrom & hopRoutine + // Set closed flag to true to prevent double close + if c.prevConn != nil { + _ = c.prevConn.Close() + } + err := c.currentConn.Close() + close(c.closeChan) + c.closed = true + return err +} + +func (c *ObfsUDPHopClientPacketConn) LocalAddr() net.Addr { + c.connMutex.RLock() + defer c.connMutex.RUnlock() + return c.currentConn.LocalAddr() +} + +func (c *ObfsUDPHopClientPacketConn) SetReadDeadline(t time.Time) error { + // Not supported + return nil +} + +func (c *ObfsUDPHopClientPacketConn) SetWriteDeadline(t time.Time) error { + // Not supported + return nil +} + +func (c *ObfsUDPHopClientPacketConn) SetDeadline(t time.Time) error { + err := c.SetReadDeadline(t) + if err != nil { + return err + } + return c.SetWriteDeadline(t) +} + +func (c *ObfsUDPHopClientPacketConn) SetReadBuffer(bytes int) error { + c.connMutex.Lock() + defer c.connMutex.Unlock() + c.readBufferSize = bytes + if c.prevConn != nil { + _ = trySetPacketConnReadBuffer(c.prevConn, bytes) + } + return trySetPacketConnReadBuffer(c.currentConn, bytes) +} + +func (c *ObfsUDPHopClientPacketConn) SetWriteBuffer(bytes int) error { + c.connMutex.Lock() + defer c.connMutex.Unlock() + c.writeBufferSize = bytes + if c.prevConn != nil { + _ = trySetPacketConnWriteBuffer(c.prevConn, bytes) + } + return trySetPacketConnWriteBuffer(c.currentConn, bytes) +} + +func (c *ObfsUDPHopClientPacketConn) SyscallConn() (syscall.RawConn, error) { + c.connMutex.RLock() + defer c.connMutex.RUnlock() + sc, ok := c.currentConn.(syscall.Conn) + if !ok { + return nil, errors.New("not supported") + } + return sc.SyscallConn() +} + +func trySetPacketConnReadBuffer(pc net.PacketConn, bytes int) error { + sc, ok := pc.(interface { + SetReadBuffer(bytes int) error + }) + if ok { + return sc.SetReadBuffer(bytes) + } + return nil +} + +func trySetPacketConnWriteBuffer(pc net.PacketConn, bytes int) error { + sc, ok := pc.(interface { + SetWriteBuffer(bytes int) error + }) + if ok { + return sc.SetWriteBuffer(bytes) + } + return nil +} + +// parseAddr parses the multi-port server address and returns the host and ports. +// Supports both comma-separated single ports and dash-separated port ranges. +// Format: "host:port1,port2-port3,port4" +func parseAddr(addr string) (host string, ports []uint16, err error) { + host, portStr, err := net.SplitHostPort(addr) + if err != nil { + return "", nil, err + } + portStrs := strings.Split(portStr, ",") + for _, portStr := range portStrs { + if strings.Contains(portStr, "-") { + // Port range + portRange := strings.Split(portStr, "-") + if len(portRange) != 2 { + return "", nil, net.InvalidAddrError("invalid port range") + } + start, err := strconv.ParseUint(portRange[0], 10, 16) + if err != nil { + return "", nil, net.InvalidAddrError("invalid port range") + } + end, err := strconv.ParseUint(portRange[1], 10, 16) + if err != nil { + return "", nil, net.InvalidAddrError("invalid port range") + } + if start > end { + start, end = end, start + } + for i := start; i <= end; i++ { + ports = append(ports, uint16(i)) + } + } else { + // Single port + port, err := strconv.ParseUint(portStr, 10, 16) + if err != nil { + return "", nil, net.InvalidAddrError("invalid port") + } + ports = append(ports, uint16(port)) + } + } + return host, ports, nil +} diff --git a/transport/hysteria/core/client.go b/transport/hysteria/core/client.go index 8a19570b..e98a0c6b 100644 --- a/transport/hysteria/core/client.go +++ b/transport/hysteria/core/client.go @@ -6,18 +6,20 @@ import ( "crypto/tls" "errors" "fmt" - "github.com/Dreamacro/clash/transport/hysteria/obfs" - "github.com/Dreamacro/clash/transport/hysteria/pmtud_fix" - "github.com/Dreamacro/clash/transport/hysteria/transport" - "github.com/Dreamacro/clash/transport/hysteria/utils" - "github.com/lunixbochs/struc" - "github.com/metacubex/quic-go" - "github.com/metacubex/quic-go/congestion" "math/rand" "net" "strconv" "sync" "time" + + "github.com/lunixbochs/struc" + "github.com/metacubex/quic-go" + "github.com/metacubex/quic-go/congestion" + + "github.com/Dreamacro/clash/transport/hysteria/obfs" + "github.com/Dreamacro/clash/transport/hysteria/pmtud_fix" + "github.com/Dreamacro/clash/transport/hysteria/transport" + "github.com/Dreamacro/clash/transport/hysteria/utils" ) var ( @@ -45,12 +47,13 @@ type Client struct { udpSessionMutex sync.RWMutex udpSessionMap map[uint32]chan *udpMessage udpDefragger defragger + hopInterval time.Duration fastOpen bool } func NewClient(serverAddr string, protocol string, auth []byte, tlsConfig *tls.Config, quicConfig *quic.Config, transport *transport.ClientTransport, sendBPS uint64, recvBPS uint64, congestionFactory CongestionFactory, - obfuscator obfs.Obfuscator,fastOpen bool) (*Client, error) { + obfuscator obfs.Obfuscator, hopInterval time.Duration, fastOpen bool) (*Client, error) { quicConfig.DisablePathMTUDiscovery = quicConfig.DisablePathMTUDiscovery || pmtud_fix.DisablePathMTUDiscovery c := &Client{ transport: transport, @@ -63,13 +66,14 @@ func NewClient(serverAddr string, protocol string, auth []byte, tlsConfig *tls.C obfuscator: obfuscator, tlsConfig: tlsConfig, quicConfig: quicConfig, - fastOpen: fastOpen, + hopInterval: hopInterval, + fastOpen: fastOpen, } return c, nil } -func (c *Client) connectToServer(dialer transport.PacketDialer) error { - qs, err := c.transport.QUICDial(c.protocol, c.serverAddr, c.tlsConfig, c.quicConfig, c.obfuscator, dialer) +func (c *Client) connectToServer(dialer utils.PacketDialer) error { + qs, err := c.transport.QUICDial(c.protocol, c.serverAddr, c.tlsConfig, c.quicConfig, c.obfuscator, c.hopInterval, dialer) if err != nil { return err } @@ -156,7 +160,7 @@ func (c *Client) handleMessage(qs quic.Connection) { } } -func (c *Client) openStreamWithReconnect(dialer transport.PacketDialer) (quic.Connection, quic.Stream, error) { +func (c *Client) openStreamWithReconnect(dialer utils.PacketDialer) (quic.Connection, quic.Stream, error) { c.reconnectMutex.Lock() defer c.reconnectMutex.Unlock() if c.closed { @@ -188,7 +192,7 @@ func (c *Client) openStreamWithReconnect(dialer transport.PacketDialer) (quic.Co return c.quicSession, &wrappedQUICStream{stream}, err } -func (c *Client) DialTCP(addr string, dialer transport.PacketDialer) (net.Conn, error) { +func (c *Client) DialTCP(addr string, dialer utils.PacketDialer) (net.Conn, error) { host, port, err := utils.SplitHostPort(addr) if err != nil { return nil, err @@ -227,11 +231,11 @@ func (c *Client) DialTCP(addr string, dialer transport.PacketDialer) (net.Conn, Orig: stream, PseudoLocalAddr: session.LocalAddr(), PseudoRemoteAddr: session.RemoteAddr(), - Established: !c.fastOpen, + Established: !c.fastOpen, }, nil } -func (c *Client) DialUDP(dialer transport.PacketDialer) (UDPConn, error) { +func (c *Client) DialUDP(dialer utils.PacketDialer) (UDPConn, error) { session, stream, err := c.openStreamWithReconnect(dialer) if err != nil { return nil, err diff --git a/transport/hysteria/transport/client.go b/transport/hysteria/transport/client.go index a48e9bf5..c30377a3 100644 --- a/transport/hysteria/transport/client.go +++ b/transport/hysteria/transport/client.go @@ -1,31 +1,41 @@ package transport import ( - "context" "crypto/tls" "fmt" + "net" + "strings" + "time" + + "github.com/metacubex/quic-go" + "github.com/Dreamacro/clash/transport/hysteria/conns/faketcp" "github.com/Dreamacro/clash/transport/hysteria/conns/udp" "github.com/Dreamacro/clash/transport/hysteria/conns/wechat" obfsPkg "github.com/Dreamacro/clash/transport/hysteria/obfs" - "github.com/metacubex/quic-go" - "net" + "github.com/Dreamacro/clash/transport/hysteria/utils" ) type ClientTransport struct { Dialer *net.Dialer } -func (ct *ClientTransport) quicPacketConn(proto string, server string, obfs obfsPkg.Obfuscator, dialer PacketDialer) (net.PacketConn, error) { +func (ct *ClientTransport) quicPacketConn(proto string, server string, obfs obfsPkg.Obfuscator, hopInterval time.Duration, dialer utils.PacketDialer) (net.PacketConn, error) { if len(proto) == 0 || proto == "udp" { conn, err := dialer.ListenPacket() if err != nil { return nil, err } if obfs != nil { + if isMultiPortAddr(server) { + return udp.NewObfsUDPHopClientPacketConn(server, hopInterval, obfs, dialer) + } oc := udp.NewObfsUDPConn(conn, obfs) return oc, nil } else { + if isMultiPortAddr(server) { + return udp.NewObfsUDPHopClientPacketConn(server, hopInterval, nil, dialer) + } return conn, nil } } else if proto == "wechat-video" { @@ -54,19 +64,13 @@ func (ct *ClientTransport) quicPacketConn(proto string, server string, obfs obfs } } -type PacketDialer interface { - ListenPacket() (net.PacketConn, error) - Context() context.Context - RemoteAddr(host string) (net.Addr, error) -} - -func (ct *ClientTransport) QUICDial(proto string, server string, tlsConfig *tls.Config, quicConfig *quic.Config, obfs obfsPkg.Obfuscator, dialer PacketDialer) (quic.Connection, error) { +func (ct *ClientTransport) QUICDial(proto string, server string, tlsConfig *tls.Config, quicConfig *quic.Config, obfs obfsPkg.Obfuscator, hopInterval time.Duration, dialer utils.PacketDialer) (quic.Connection, error) { serverUDPAddr, err := dialer.RemoteAddr(server) if err != nil { return nil, err } - pktConn, err := ct.quicPacketConn(proto, serverUDPAddr.String(), obfs, dialer) + pktConn, err := ct.quicPacketConn(proto, serverUDPAddr.String(), obfs, hopInterval, dialer) if err != nil { return nil, err } @@ -90,3 +94,11 @@ func (ct *ClientTransport) DialTCP(raddr *net.TCPAddr) (*net.TCPConn, error) { func (ct *ClientTransport) ListenUDP() (*net.UDPConn, error) { return net.ListenUDP("udp", nil) } + +func isMultiPortAddr(addr string) bool { + _, portStr, err := net.SplitHostPort(addr) + if err == nil && (strings.Contains(portStr, ",") || strings.Contains(portStr, "-")) { + return true + } + return false +} diff --git a/transport/hysteria/utils/misc.go b/transport/hysteria/utils/misc.go index 29c7cf0c..5d5159fc 100644 --- a/transport/hysteria/utils/misc.go +++ b/transport/hysteria/utils/misc.go @@ -1,6 +1,7 @@ package utils import ( + "context" "net" "strconv" ) @@ -40,3 +41,9 @@ func last(s string, b byte) int { } return i } + +type PacketDialer interface { + ListenPacket() (net.PacketConn, error) + Context() context.Context + RemoteAddr(host string) (net.Addr, error) +} From 0da09c5dddbe190b1efbc85f8a37ea9231cdc61b Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 27 Nov 2022 11:42:43 +0800 Subject: [PATCH 164/250] fix: quicStreamPacketConn's close --- transport/tuic/client.go | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/transport/tuic/client.go b/transport/tuic/client.go index ca0e78a1..f30932f7 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -401,7 +401,6 @@ func (t *Client) ListenPacketContext(ctx context.Context, metadata *C.Metadata) } pipe1, pipe2 := net.Pipe() - inputCh := make(chan udpData) var connId uint32 for { connId = rand.Uint32() @@ -416,24 +415,16 @@ func (t *Client) ListenPacketContext(ctx context.Context, metadata *C.Metadata) lAddr: quicConn.LocalAddr(), client: t, inputConn: N.NewBufferedConn(pipe2), - inputCh: inputCh, } return pc, nil } -type udpData struct { - data []byte - addr net.Addr - err error -} - type quicStreamPacketConn struct { connId uint32 quicConn quic.Connection lAddr net.Addr client *Client inputConn *N.BufferedConn - inputCh chan udpData closeOnce sync.Once closeErr error @@ -455,6 +446,8 @@ func (q *quicStreamPacketConn) close() (err error) { defer func() { q.client.deferQuicConn(q.quicConn, err) }() + q.client.udpInputMap.Delete(q.connId) + _ = q.inputConn.Close() buf := &bytes.Buffer{} err = NewDissociate(q.connId).WriteTo(buf) if err != nil { From b80e7c3c920037d99be7be8024732c16c8fd4447 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 27 Nov 2022 12:06:04 +0800 Subject: [PATCH 165/250] fix: tuic client use pool-based bytes.Buffer --- transport/tuic/client.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/transport/tuic/client.go b/transport/tuic/client.go index f30932f7..e31cdb82 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -18,6 +18,7 @@ import ( "github.com/metacubex/quic-go" N "github.com/Dreamacro/clash/common/net" + "github.com/Dreamacro/clash/common/pool" "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/transport/tuic/congestion" @@ -119,7 +120,8 @@ func (t *Client) getQuicConn(ctx context.Context) (quic.Connection, error) { if err != nil { return err } - buf := &bytes.Buffer{} + buf := pool.GetBuffer() + defer pool.PutBuffer(buf) err = NewAuthenticate(t.Token).WriteTo(buf) if err != nil { return err @@ -262,7 +264,8 @@ func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata) (net.Con defer func() { t.deferQuicConn(quicConn, err) }() - buf := &bytes.Buffer{} + buf := pool.GetBuffer() + defer pool.PutBuffer(buf) err = NewConnect(NewAddress(metadata)).WriteTo(buf) if err != nil { return nil, err @@ -448,7 +451,8 @@ func (q *quicStreamPacketConn) close() (err error) { }() q.client.udpInputMap.Delete(q.connId) _ = q.inputConn.Close() - buf := &bytes.Buffer{} + buf := pool.GetBuffer() + defer pool.PutBuffer(buf) err = NewDissociate(q.connId).WriteTo(buf) if err != nil { return @@ -503,7 +507,8 @@ func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err erro q.client.deferQuicConn(q.quicConn, err) }() addr.String() - buf := &bytes.Buffer{} + buf := pool.GetBuffer() + defer pool.PutBuffer(buf) addrPort, err := netip.ParseAddrPort(addr.String()) if err != nil { return From 6b1ca7b07c98f116a38aa865924951529879f21c Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sun, 27 Nov 2022 12:52:14 +0800 Subject: [PATCH 166/250] fix: ports of hysteria is optional --- adapter/outbound/hysteria.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index fd441dc1..e6c2b25d 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -91,7 +91,7 @@ type HysteriaOption struct { Name string `proxy:"name"` Server string `proxy:"server"` Port int `proxy:"port"` - Ports string `proxy:"ports"` + Ports string `proxy:"ports,omitempty"` Protocol string `proxy:"protocol,omitempty"` ObfsProtocol string `proxy:"obfs-protocol,omitempty"` // compatible with Stash Up string `proxy:"up"` @@ -137,7 +137,7 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { }, } var addr string - if option.Ports == "" { + if len(option.Ports) == 0 { addr = net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) } else { addr = net.JoinHostPort(option.Server, option.Ports) From c8d7243b5b4ab13d13117f60fb7153fc0482a436 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 27 Nov 2022 13:07:27 +0800 Subject: [PATCH 167/250] fix: hop-interval of tysteria is optional too --- adapter/outbound/hysteria.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index e6c2b25d..be285245 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -112,7 +112,7 @@ type HysteriaOption struct { ReceiveWindow int `proxy:"recv-window,omitempty"` DisableMTUDiscovery bool `proxy:"disable-mtu-discovery,omitempty"` FastOpen bool `proxy:"fast-open,omitempty"` - HopInterval int `proxy:"hop-interval"` + HopInterval int `proxy:"hop-interval,omitempty"` } func (c *HysteriaOption) Speed() (uint64, uint64, error) { From a58234f0cd40d7de8f0c42ed4fcb344a5cc24b80 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 27 Nov 2022 13:44:38 +0800 Subject: [PATCH 168/250] chore: support KeyReplacer in Structure Decoder --- adapter/outbound/hysteria.go | 1 - adapter/parser.go | 10 +++------- common/structure/structure.go | 24 ++++++++++++++++++++++++ 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index be285245..932e8feb 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -99,7 +99,6 @@ type HysteriaOption struct { Down string `proxy:"down"` DownSpeed int `proxy:"down-speed,omitempty"` // compatible with Stash Auth string `proxy:"auth,omitempty"` - OldAuthString string `proxy:"auth_str,omitempty"` AuthString string `proxy:"auth-str,omitempty"` Obfs string `proxy:"obfs,omitempty"` SNI string `proxy:"sni,omitempty"` diff --git a/adapter/parser.go b/adapter/parser.go index c0ed8792..3ca0c3ee 100644 --- a/adapter/parser.go +++ b/adapter/parser.go @@ -9,14 +9,10 @@ import ( C "github.com/Dreamacro/clash/constant" ) -func ParseProxy(mapping map[string]any) (C.Proxy, error) { - newMapping := make(map[string]any) - for key := range mapping { - newMapping[strings.ReplaceAll(key, "_", "-")] = mapping[key] - } - mapping = newMapping +var keyReplacer = strings.NewReplacer("_", "-") - decoder := structure.NewDecoder(structure.Option{TagName: "proxy", WeaklyTypedInput: true}) +func ParseProxy(mapping map[string]any) (C.Proxy, error) { + decoder := structure.NewDecoder(structure.Option{TagName: "proxy", WeaklyTypedInput: true, KeyReplacer: keyReplacer}) proxyType, existType := mapping["type"].(string) if !existType { return nil, fmt.Errorf("missing type") diff --git a/common/structure/structure.go b/common/structure/structure.go index e74c349a..2b5e024f 100644 --- a/common/structure/structure.go +++ b/common/structure/structure.go @@ -13,6 +13,7 @@ import ( type Option struct { TagName string WeaklyTypedInput bool + KeyReplacer *strings.Replacer } // Decoder is the core of structure @@ -48,7 +49,24 @@ func (d *Decoder) Decode(src map[string]any, dst any) error { key, omitKey, found := strings.Cut(tag, ",") omitempty := found && omitKey == "omitempty" + if d.option.KeyReplacer != nil { + key = d.option.KeyReplacer.Replace(key) + } + value, ok := src[key] + if !ok { + for _strKey := range src { + strKey := _strKey + if d.option.KeyReplacer != nil { + strKey = d.option.KeyReplacer.Replace(strKey) + } + if strings.EqualFold(key, strKey) { + value = src[_strKey] + ok = true + break + } + } + } if !ok || value == nil { if omitempty { continue @@ -346,6 +364,9 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e tagValue = strings.SplitN(tagValue, ",", 2)[0] if tagValue != "" { fieldName = tagValue + if d.option.KeyReplacer != nil { + fieldName = d.option.KeyReplacer.Replace(fieldName) + } } rawMapKey := reflect.ValueOf(fieldName) @@ -359,6 +380,9 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e // Not a string key continue } + if d.option.KeyReplacer != nil { + mK = d.option.KeyReplacer.Replace(mK) + } if strings.EqualFold(mK, fieldName) { rawMapKey = dataValKey From cd53e2d4a7a1c936b60877bb8df8638b07d0b87c Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 27 Nov 2022 16:38:41 +0800 Subject: [PATCH 169/250] fix: set RequestTimeout in earlyConn --- transport/tuic/client.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/transport/tuic/client.go b/transport/tuic/client.go index e31cdb82..a2057bd1 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -291,10 +291,7 @@ func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata) (net.Con return nil, err } - if t.RequestTimeout > 0 { - _ = stream.SetReadDeadline(time.Now().Add(time.Duration(t.RequestTimeout) * time.Millisecond)) - } - conn := &earlyConn{BufferedConn: N.NewBufferedConn(stream)} + conn := &earlyConn{BufferedConn: N.NewBufferedConn(stream), RequestTimeout: t.RequestTimeout} if !t.FastOpen { err = conn.Response() if err != nil { @@ -308,9 +305,14 @@ type earlyConn struct { *N.BufferedConn resOnce sync.Once resErr error + + RequestTimeout int } func (conn *earlyConn) response() error { + if conn.RequestTimeout > 0 { + _ = conn.SetReadDeadline(time.Now().Add(time.Duration(conn.RequestTimeout) * time.Millisecond)) + } response, err := ReadResponse(conn) if err != nil { _ = conn.Close() From 551283c16ea04f55e6622f1372d40f0c52ef488f Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 28 Nov 2022 17:09:25 +0800 Subject: [PATCH 170/250] chore: add tuic-server listener --- adapter/outbound/tuic.go | 2 +- config/config.go | 70 ++++- constant/metadata.go | 13 + docs/config.yaml | 15 ++ hub/executor/executor.go | 1 + hub/route/configs.go | 4 + listener/listener.go | 42 +++ listener/tuic/server.go | 132 ++++++++++ transport/tuic/client.go | 479 +++++++++++----------------------- transport/tuic/conn.go | 250 ++++++++++++++++++ transport/tuic/pool_client.go | 5 +- transport/tuic/protocol.go | 88 +++++++ transport/tuic/server.go | 292 +++++++++++++++++++++ 13 files changed, 1063 insertions(+), 330 deletions(-) create mode 100644 listener/tuic/server.go create mode 100644 transport/tuic/conn.go create mode 100644 transport/tuic/server.go diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index 10fbfd2e..97938014 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -212,7 +212,7 @@ func NewTuic(option TuicOption) (*Tuic, error) { UdpRelayMode: option.UdpRelayMode, CongestionController: option.CongestionController, ReduceRtt: option.ReduceRtt, - RequestTimeout: option.RequestTimeout, + RequestTimeout: time.Duration(option.RequestTimeout) * time.Millisecond, MaxUdpRelayPacketSize: option.MaxUdpRelayPacketSize, FastOpen: option.FastOpen, MaxOpenStreams: clientMaxOpenStreams, diff --git a/config/config.go b/config/config.go index d3733bed..9c468921 100644 --- a/config/config.go +++ b/config/config.go @@ -54,6 +54,7 @@ type General struct { TCPConcurrent bool `json:"tcp-concurrent"` EnableProcess bool `json:"enable-process"` Tun Tun `json:"tun"` + TuicServer TuicServer `json:"tuic-server"` Sniffing bool `json:"sniffing"` EBpf EBpf `json:"-"` } @@ -114,6 +115,24 @@ type Profile struct { StoreFakeIP bool `yaml:"store-fake-ip"` } +type TuicServer struct { + Enable bool `yaml:"enable" json:"enable"` + Listen string `yaml:"listen" json:"listen"` + Token []string `yaml:"token" json:"token"` + Certificate string `yaml:"certificate" json:"certificate"` + PrivateKey string `yaml:"private-key" json:"private-key"` + CongestionController string `yaml:"congestion-controller" json:"congestion-controller,omitempty"` + MaxIdleTime int `yaml:"max-idle-time" json:"max-idle-time,omitempty"` + AuthenticationTimeout int `yaml:"authentication-timeout" json:"authentication-timeout,omitempty"` + ALPN []string `yaml:"alpn" json:"alpn,omitempty"` + MaxUdpRelayPacketSize int `yaml:"max-udp-relay-packet-size" json:"max-udp-relay-packet-size,omitempty"` +} + +func (t TuicServer) String() string { + b, _ := json.Marshal(t) + return string(b) +} + // Tun config type Tun struct { Enable bool `yaml:"enable" json:"enable"` @@ -282,6 +301,19 @@ type RawTun struct { UDPTimeout int64 `yaml:"udp-timeout" json:"udp_timeout,omitempty"` } +type RawTuicServer struct { + Enable bool `yaml:"enable" json:"enable"` + Listen string `yaml:"listen" json:"listen"` + Token []string `yaml:"token" json:"token"` + Certificate string `yaml:"certificate" json:"certificate"` + PrivateKey string `yaml:"private-key" json:"private-key"` + CongestionController string `yaml:"congestion-controller" json:"congestion-controller,omitempty"` + MaxIdleTime int `yaml:"max-idle-time" json:"max-idle-time,omitempty"` + AuthenticationTimeout int `yaml:"authentication-timeout" json:"authentication-timeout,omitempty"` + ALPN []string `yaml:"alpn" json:"alpn,omitempty"` + MaxUdpRelayPacketSize int `yaml:"max-udp-relay-packet-size" json:"max-udp-relay-packet-size,omitempty"` +} + type RawConfig struct { Port int `yaml:"port"` SocksPort int `yaml:"socks-port"` @@ -316,6 +348,7 @@ type RawConfig struct { Hosts map[string]string `yaml:"hosts"` DNS RawDNS `yaml:"dns"` Tun RawTun `yaml:"tun"` + TuicServer RawTuicServer `yaml:"tuic-server"` EBpf EBpf `yaml:"ebpf"` IPTables IPTables `yaml:"iptables"` Experimental Experimental `yaml:"experimental"` @@ -392,6 +425,18 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { AutoDetectInterface: true, Inet6Address: []ListenPrefix{ListenPrefix(netip.MustParsePrefix("fdfe:dcba:9876::1/126"))}, }, + TuicServer: RawTuicServer{ + Enable: false, + Token: nil, + Certificate: "", + PrivateKey: "", + Listen: "", + CongestionController: "", + MaxIdleTime: 15000, + AuthenticationTimeout: 1000, + ALPN: []string{"h3"}, + MaxUdpRelayPacketSize: 1500, + }, EBpf: EBpf{ RedirectToTun: []string{}, AutoRedir: []string{}, @@ -508,6 +553,11 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { return nil, err } + err = parseTuicServer(rawCfg.TuicServer, config.General) + if err != nil { + return nil, err + } + config.Users = parseAuthentication(rawCfg.Authentication) config.Sniffer, err = parseSniffer(rawCfg.Sniffer) @@ -905,8 +955,8 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error) if _, _, err := net.SplitHostPort(host); err != nil && strings.Contains(err.Error(), "missing port in address") { host = net.JoinHostPort(host, "443") } else { - if err!=nil{ - return nil,err + if err != nil { + return nil, err } } clearURL := url.URL{Scheme: "https", Host: host, Path: u.Path} @@ -1197,6 +1247,22 @@ func parseTun(rawTun RawTun, general *General) error { return nil } +func parseTuicServer(rawTuic RawTuicServer, general *General) error { + general.TuicServer = TuicServer{ + Enable: rawTuic.Enable, + Listen: rawTuic.Listen, + Token: rawTuic.Token, + Certificate: rawTuic.Certificate, + PrivateKey: rawTuic.PrivateKey, + CongestionController: rawTuic.CongestionController, + MaxIdleTime: rawTuic.MaxIdleTime, + AuthenticationTimeout: rawTuic.AuthenticationTimeout, + ALPN: rawTuic.ALPN, + MaxUdpRelayPacketSize: rawTuic.MaxUdpRelayPacketSize, + } + return nil +} + func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) { sniffer := &Sniffer{ Enable: snifferRaw.Enable, diff --git a/constant/metadata.go b/constant/metadata.go index 67a42186..ce394834 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -27,6 +27,7 @@ const ( TCPTUN UDPTUN TUN + TUIC INNER ) @@ -71,6 +72,8 @@ func (t Type) String() string { return "UdpTun" case TUN: return "Tun" + case TUIC: + return "Tuic" case INNER: return "Inner" default: @@ -89,12 +92,22 @@ func ParseType(t string) (*Type, error) { res = SOCKS4 case "SOCKS5": res = SOCKS5 + case "SHADOWSOCKS": + res = SHADOWSOCKS + case "VMESS": + res = VMESS case "REDIR": res = REDIR case "TPROXY": res = TPROXY + case "TCPTUN": + res = TCPTUN + case "UDPTUN": + res = UDPTUN case "TUN": res = TUN + case "TUIC": + res = TUIC case "INNER": res = INNER default: diff --git a/docs/config.yaml b/docs/config.yaml index e7789dfb..f8d06e91 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -103,6 +103,21 @@ sniffer: # tcptun-config: 127.0.0.1:801=www.example.com:80,127.0.0.1:4431=www.example.com:443 # udptun-config: 127.0.0.1:531=8.8.8.8:53,127.0.0.1:532=1.1.1.1:53 +# tuic服务器入口(传入流量将和socks,mixed等入口一样按照mode所指定的方式进行匹配处理) +#tuic-server: +# enable: true +# listen: 127.0.0.1:10443 +# token: +# - TOKEN +# certificate: ./server.crt +# private-key: ./server.key +# congestion-controller: bbr +# max-idle-time: 15000 +# authentication-timeout: 1000 +# alpn: +# - h3 +# max-udp-relay-packet-size: 1500 + profile: # 存储select选择记录 store-selected: false diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 4c77aaba..dfd3a75d 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -351,6 +351,7 @@ func updateGeneral(general *config.General, force bool) { P.ReCreateVmess(general.VmessConfig, tcpIn, udpIn) P.ReCreateTcpTun(general.TcpTunConfig, tcpIn, udpIn) P.ReCreateUdpTun(general.UdpTunConfig, tcpIn, udpIn) + P.ReCreateTuic(general.TuicServer, tcpIn, udpIn) } func updateUsers(users []auth.AuthUser) { diff --git a/hub/route/configs.go b/hub/route/configs.go index 1e08dbda..3563555c 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -41,6 +41,7 @@ type configSchema struct { TProxyPort *int `json:"tproxy-port"` MixedPort *int `json:"mixed-port"` Tun *tunSchema `json:"tun"` + TuicServer *config.TuicServer `json:"tuic-server"` ShadowSocksConfig *string `json:"ss-config"` VmessConfig *string `json:"vmess-config"` TcptunConfig *string `json:"tcptun-config"` @@ -203,6 +204,9 @@ func patchConfigs(w http.ResponseWriter, r *http.Request) { P.ReCreateVmess(pointerOrDefaultString(general.VmessConfig, ports.VmessConfig), tcpIn, udpIn) P.ReCreateTcpTun(pointerOrDefaultString(general.TcptunConfig, ports.TcpTunConfig), tcpIn, udpIn) P.ReCreateUdpTun(pointerOrDefaultString(general.UdptunConfig, ports.UdpTunConfig), tcpIn, udpIn) + if general.TuicServer != nil { + P.ReCreateTuic(*general.TuicServer, tcpIn, udpIn) + } if general.Mode != nil { tunnel.SetMode(*general.Mode) diff --git a/listener/listener.go b/listener/listener.go index dfae99dc..ff8fb37c 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -21,6 +21,7 @@ import ( "github.com/Dreamacro/clash/listener/sing_vmess" "github.com/Dreamacro/clash/listener/socks" "github.com/Dreamacro/clash/listener/tproxy" + "github.com/Dreamacro/clash/listener/tuic" "github.com/Dreamacro/clash/listener/tunnel" "github.com/Dreamacro/clash/log" ) @@ -43,6 +44,7 @@ var ( vmessListener *sing_vmess.Listener tcpTunListener *tunnel.Listener udpTunListener *tunnel.UdpListener + tuicListener *tuic.Listener autoRedirListener *autoredir.Listener autoRedirProgram *ebpf.TcEBpfProgram tcProgram *ebpf.TcEBpfProgram @@ -58,6 +60,7 @@ var ( vmessMux sync.Mutex tcpTunMux sync.Mutex udpTunMux sync.Mutex + tuicMux sync.Mutex autoRedirMux sync.Mutex tcMux sync.Mutex @@ -390,6 +393,45 @@ func ReCreateUdpTun(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inb return } +func ReCreateTuic(config config.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { + tuicMux.Lock() + defer tuicMux.Unlock() + shouldIgnore := false + + var err error + defer func() { + if err != nil { + log.Errorln("Start Tuic server error: %s", err.Error()) + } + }() + + if tuicListener != nil { + if tuicListener.Config().String() != config.String() { + tuicListener.Close() + tuicListener = nil + } else { + shouldIgnore = true + } + } + + if shouldIgnore { + return + } + + if !config.Enable { + return + } + + listener, err := tuic.New(config, tcpIn, udpIn) + if err != nil { + return + } + + tuicListener = listener + + return +} + func ReCreateTProxy(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { tproxyMux.Lock() defer tproxyMux.Unlock() diff --git a/listener/tuic/server.go b/listener/tuic/server.go new file mode 100644 index 00000000..3ea2dd80 --- /dev/null +++ b/listener/tuic/server.go @@ -0,0 +1,132 @@ +package tuic + +import ( + "crypto/tls" + "net" + "strings" + "time" + + "github.com/metacubex/quic-go" + + "github.com/Dreamacro/clash/adapter/inbound" + "github.com/Dreamacro/clash/common/sockopt" + "github.com/Dreamacro/clash/config" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/log" + "github.com/Dreamacro/clash/transport/socks5" + "github.com/Dreamacro/clash/transport/tuic" +) + +const ( + DefaultStreamReceiveWindow = 15728640 // 15 MB/s + DefaultConnectionReceiveWindow = 67108864 // 64 MB/s +) + +type Listener struct { + closed bool + config config.TuicServer + udpListeners []net.PacketConn + servers []*tuic.Server +} + +func New(config config.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (*Listener, error) { + cert, err := tls.LoadX509KeyPair(config.Certificate, config.PrivateKey) + if err != nil { + return nil, err + } + tlsConfig := &tls.Config{ + MinVersion: tls.VersionTLS13, + Certificates: []tls.Certificate{cert}, + } + if len(config.ALPN) > 0 { + tlsConfig.NextProtos = config.ALPN + } else { + tlsConfig.NextProtos = []string{"h3"} + } + quicConfig := &quic.Config{ + MaxIdleTimeout: time.Duration(config.MaxIdleTime) * time.Millisecond, + MaxIncomingStreams: 1 >> 32, + MaxIncomingUniStreams: 1 >> 32, + EnableDatagrams: true, + } + quicConfig.InitialStreamReceiveWindow = DefaultStreamReceiveWindow / 10 + quicConfig.MaxStreamReceiveWindow = DefaultStreamReceiveWindow + quicConfig.InitialConnectionReceiveWindow = DefaultConnectionReceiveWindow / 10 + quicConfig.MaxConnectionReceiveWindow = DefaultConnectionReceiveWindow + + tokens := make([][32]byte, len(config.Token)) + for i, token := range config.Token { + tokens[i] = tuic.GenTKN(token) + } + + option := &tuic.ServerOption{ + HandleTcpFn: func(conn net.Conn, addr string) error { + tcpIn <- inbound.NewSocket(socks5.ParseAddr(addr), conn, C.TUIC) + return nil + }, + HandleUdpFn: func(addr *net.UDPAddr, packet C.UDPPacket) error { + select { + case udpIn <- inbound.NewPacket(socks5.ParseAddrToSocksAddr(addr), packet, C.TUIC): + default: + } + return nil + }, + TlsConfig: tlsConfig, + QuicConfig: quicConfig, + Tokens: tokens, + CongestionController: config.CongestionController, + AuthenticationTimeout: time.Duration(config.AuthenticationTimeout) * time.Millisecond, + MaxUdpRelayPacketSize: config.MaxUdpRelayPacketSize, + } + + sl := &Listener{false, config, nil, nil} + + for _, addr := range strings.Split(config.Listen, ",") { + addr := addr + + ul, err := net.ListenPacket("udp", addr) + if err != nil { + return nil, err + } + + err = sockopt.UDPReuseaddr(ul.(*net.UDPConn)) + if err != nil { + log.Warnln("Failed to Reuse UDP Address: %s", err) + } + + sl.udpListeners = append(sl.udpListeners, ul) + + server, err := tuic.NewServer(option, ul) + if err != nil { + return nil, err + } + + sl.servers = append(sl.servers, server) + + go func() { + log.Infoln("Tuic proxy listening at: %s", ul.LocalAddr().String()) + err := server.Serve() + if err != nil { + if sl.closed { + return + } + } + }() + } + + return sl, nil +} + +func (l *Listener) Close() { + l.closed = true + for _, lis := range l.servers { + _ = lis.Close() + } + for _, lis := range l.udpListeners { + _ = lis.Close() + } +} + +func (l *Listener) Config() config.TuicServer { + return l.config +} diff --git a/transport/tuic/client.go b/transport/tuic/client.go index a2057bd1..a1dfcc30 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -6,10 +6,8 @@ import ( "context" "crypto/tls" "errors" - "fmt" "math/rand" "net" - "net/netip" "runtime" "sync" "sync/atomic" @@ -21,7 +19,6 @@ import ( "github.com/Dreamacro/clash/common/pool" "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" - "github.com/Dreamacro/clash/transport/tuic/congestion" ) var ( @@ -39,7 +36,7 @@ type ClientOption struct { UdpRelayMode string CongestionController string ReduceRtt bool - RequestTimeout int + RequestTimeout time.Duration MaxUdpRelayPacketSize int FastOpen bool MaxOpenStreams int64 @@ -53,6 +50,7 @@ type Client struct { connMutex sync.Mutex openStreams atomic.Int64 + closed atomic.Bool udpInputMap sync.Map @@ -82,160 +80,141 @@ func (t *Client) getQuicConn(ctx context.Context) (quic.Connection, error) { return nil, err } - switch t.CongestionController { - case "cubic": - quicConn.SetCongestionControl( - congestion.NewCubicSender( - congestion.DefaultClock{}, - congestion.GetMaxPacketSize(quicConn.RemoteAddr()), - false, - nil, - ), - ) - case "new_reno": - quicConn.SetCongestionControl( - congestion.NewCubicSender( - congestion.DefaultClock{}, - congestion.GetMaxPacketSize(quicConn.RemoteAddr()), - true, - nil, - ), - ) - case "bbr": - quicConn.SetCongestionControl( - congestion.NewBBRSender( - congestion.DefaultClock{}, - congestion.GetMaxPacketSize(quicConn.RemoteAddr()), - congestion.InitialCongestionWindow, - congestion.DefaultBBRMaxCongestionWindow, - ), - ) - } + SetCongestionController(quicConn, t.CongestionController) - sendAuthentication := func(quicConn quic.Connection) (err error) { - defer func() { - t.deferQuicConn(quicConn, err) - }() - stream, err := quicConn.OpenUniStream() - if err != nil { - return err - } - buf := pool.GetBuffer() - defer pool.PutBuffer(buf) - err = NewAuthenticate(t.Token).WriteTo(buf) - if err != nil { - return err - } - _, err = buf.WriteTo(stream) - if err != nil { - return err - } - err = stream.Close() - if err != nil { - return - } - return nil - } - - parseUDP := func(quicConn quic.Connection) (err error) { - defer func() { - t.deferQuicConn(quicConn, err) - }() - switch t.UdpRelayMode { - case "quic": - for { - var stream quic.ReceiveStream - stream, err = quicConn.AcceptUniStream(context.Background()) - if err != nil { - return err - } - go func() (err error) { - var assocId uint32 - defer func() { - t.deferQuicConn(quicConn, err) - if err != nil && assocId != 0 { - if val, ok := t.udpInputMap.LoadAndDelete(assocId); ok { - if conn, ok := val.(net.Conn); ok { - _ = conn.Close() - } - } - } - stream.CancelRead(0) - }() - reader := bufio.NewReader(stream) - packet, err := ReadPacket(reader) - if err != nil { - return - } - assocId = packet.ASSOC_ID - if val, ok := t.udpInputMap.Load(assocId); ok { - if conn, ok := val.(net.Conn); ok { - writer := bufio.NewWriterSize(conn, packet.BytesLen()) - _ = packet.WriteTo(writer) - _ = writer.Flush() - } - } - return - }() - } - default: // native - for { - var message []byte - message, err = quicConn.ReceiveMessage() - if err != nil { - return err - } - go func() (err error) { - var assocId uint32 - defer func() { - t.deferQuicConn(quicConn, err) - if err != nil && assocId != 0 { - if val, ok := t.udpInputMap.LoadAndDelete(assocId); ok { - if conn, ok := val.(net.Conn); ok { - _ = conn.Close() - } - } - } - }() - buffer := bytes.NewBuffer(message) - packet, err := ReadPacket(buffer) - if err != nil { - return - } - assocId = packet.ASSOC_ID - if val, ok := t.udpInputMap.Load(assocId); ok { - if conn, ok := val.(net.Conn); ok { - _, _ = conn.Write(message) - } - } - return - }() - } - } - } - - go sendAuthentication(quicConn) + go func() { + _ = t.sendAuthentication(quicConn) + }() if t.udp { - go parseUDP(quicConn) + go func() { + _ = t.parseUDP(quicConn) + }() } t.quicConn = quicConn + t.openStreams.Store(0) return quicConn, nil } +func (t *Client) sendAuthentication(quicConn quic.Connection) (err error) { + defer func() { + t.deferQuicConn(quicConn, err) + }() + stream, err := quicConn.OpenUniStream() + if err != nil { + return err + } + buf := pool.GetBuffer() + defer pool.PutBuffer(buf) + err = NewAuthenticate(t.Token).WriteTo(buf) + if err != nil { + return err + } + _, err = buf.WriteTo(stream) + if err != nil { + return err + } + err = stream.Close() + if err != nil { + return + } + return nil +} + +func (t *Client) parseUDP(quicConn quic.Connection) (err error) { + defer func() { + t.deferQuicConn(quicConn, err) + }() + switch t.UdpRelayMode { + case "quic": + for { + var stream quic.ReceiveStream + stream, err = quicConn.AcceptUniStream(context.Background()) + if err != nil { + return err + } + go func() (err error) { + var assocId uint32 + defer func() { + t.deferQuicConn(quicConn, err) + if err != nil && assocId != 0 { + if val, ok := t.udpInputMap.LoadAndDelete(assocId); ok { + if conn, ok := val.(net.Conn); ok { + _ = conn.Close() + } + } + } + stream.CancelRead(0) + }() + reader := bufio.NewReader(stream) + packet, err := ReadPacket(reader) + if err != nil { + return + } + assocId = packet.ASSOC_ID + if val, ok := t.udpInputMap.Load(assocId); ok { + if conn, ok := val.(net.Conn); ok { + writer := bufio.NewWriterSize(conn, packet.BytesLen()) + _ = packet.WriteTo(writer) + _ = writer.Flush() + } + } + return + }() + } + default: // native + for { + var message []byte + message, err = quicConn.ReceiveMessage() + if err != nil { + return err + } + go func() (err error) { + var assocId uint32 + defer func() { + t.deferQuicConn(quicConn, err) + if err != nil && assocId != 0 { + if val, ok := t.udpInputMap.LoadAndDelete(assocId); ok { + if conn, ok := val.(net.Conn); ok { + _ = conn.Close() + } + } + } + }() + buffer := bytes.NewBuffer(message) + packet, err := ReadPacket(buffer) + if err != nil { + return + } + assocId = packet.ASSOC_ID + if val, ok := t.udpInputMap.Load(assocId); ok { + if conn, ok := val.(net.Conn); ok { + _, _ = conn.Write(message) + } + } + return + }() + } + } +} + func (t *Client) deferQuicConn(quicConn quic.Connection, err error) { var netError net.Error if err != nil && errors.As(err, &netError) { t.connMutex.Lock() defer t.connMutex.Unlock() if t.quicConn == quicConn { - t.Close(err) + t.forceClose(err, true) } } } -func (t *Client) Close(err error) { +func (t *Client) forceClose(err error, locked bool) { + if !locked { + t.connMutex.Lock() + defer t.connMutex.Unlock() + } quicConn := t.quicConn if quicConn != nil { _ = quicConn.CloseWithError(ProtocolError, err.Error()) @@ -250,6 +229,13 @@ func (t *Client) Close(err error) { } } +func (t *Client) Close() { + t.closed.Store(true) + if t.openStreams.Load() == 0 { + t.forceClose(ClientClosed, false) + } +} + func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata) (net.Conn, error) { quicConn, err := t.getQuicConn(ctx) if err != nil { @@ -278,7 +264,15 @@ func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata) (net.Con Stream: quicStream, lAddr: quicConn.LocalAddr(), rAddr: quicConn.RemoteAddr(), - client: t, + ref: t, + closeDeferFn: func() { + time.AfterFunc(C.DefaultTCPTimeout, func() { + openStreams := t.openStreams.Add(-1) + if openStreams == 0 && t.closed.Load() { + t.forceClose(ClientClosed, false) + } + }) + }, } _, err = buf.WriteTo(stream) if err != nil { @@ -306,12 +300,12 @@ type earlyConn struct { resOnce sync.Once resErr error - RequestTimeout int + RequestTimeout time.Duration } func (conn *earlyConn) response() error { if conn.RequestTimeout > 0 { - _ = conn.SetReadDeadline(time.Now().Add(time.Duration(conn.RequestTimeout) * time.Millisecond)) + _ = conn.SetReadDeadline(time.Now().Add(conn.RequestTimeout)) } response, err := ReadResponse(conn) if err != nil { @@ -341,59 +335,6 @@ func (conn *earlyConn) Read(b []byte) (n int, err error) { return conn.BufferedConn.Read(b) } -type quicStreamConn struct { - quic.Stream - lock sync.Mutex - lAddr net.Addr - rAddr net.Addr - client *Client - - closeOnce sync.Once - closeErr error -} - -func (q *quicStreamConn) Write(p []byte) (n int, err error) { - q.lock.Lock() - defer q.lock.Unlock() - return q.Stream.Write(p) -} - -func (q *quicStreamConn) Close() error { - q.closeOnce.Do(func() { - q.closeErr = q.close() - }) - return q.closeErr -} - -func (q *quicStreamConn) close() error { - defer time.AfterFunc(C.DefaultTCPTimeout, func() { - q.client.openStreams.Add(-1) - }) - - // https://github.com/cloudflare/cloudflared/commit/ed2bac026db46b239699ac5ce4fcf122d7cab2cd - // Make sure a possible writer does not block the lock forever. We need it, so we can close the writer - // side of the stream safely. - _ = q.Stream.SetWriteDeadline(time.Now()) - - // This lock is eventually acquired despite Write also acquiring it, because we set a deadline to writes. - q.lock.Lock() - defer q.lock.Unlock() - - // We have to clean up the receiving stream ourselves since the Close in the bottom does not handle that. - q.Stream.CancelRead(0) - return q.Stream.Close() -} - -func (q *quicStreamConn) LocalAddr() net.Addr { - return q.lAddr -} - -func (q *quicStreamConn) RemoteAddr() net.Addr { - return q.rAddr -} - -var _ net.Conn = &quicStreamConn{} - func (t *Client) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (net.PacketConn, error) { quicConn, err := t.getQuicConn(ctx) if err != nil { @@ -415,139 +356,27 @@ func (t *Client) ListenPacketContext(ctx context.Context, metadata *C.Metadata) } } pc := &quicStreamPacketConn{ - connId: connId, - quicConn: quicConn, - lAddr: quicConn.LocalAddr(), - client: t, - inputConn: N.NewBufferedConn(pipe2), + connId: connId, + quicConn: quicConn, + lAddr: quicConn.LocalAddr(), + inputConn: N.NewBufferedConn(pipe2), + udpRelayMode: t.UdpRelayMode, + maxUdpRelayPacketSize: t.MaxUdpRelayPacketSize, + ref: t, + deferQuicConnFn: t.deferQuicConn, + closeDeferFn: func() { + t.udpInputMap.Delete(connId) + time.AfterFunc(C.DefaultUDPTimeout, func() { + openStreams := t.openStreams.Add(-1) + if openStreams == 0 && t.closed.Load() { + t.forceClose(ClientClosed, false) + } + }) + }, } return pc, nil } -type quicStreamPacketConn struct { - connId uint32 - quicConn quic.Connection - lAddr net.Addr - client *Client - inputConn *N.BufferedConn - - closeOnce sync.Once - closeErr error - closed bool -} - -func (q *quicStreamPacketConn) Close() error { - q.closeOnce.Do(func() { - q.closed = true - q.closeErr = q.close() - }) - return q.closeErr -} - -func (q *quicStreamPacketConn) close() (err error) { - defer time.AfterFunc(C.DefaultTCPTimeout, func() { - q.client.openStreams.Add(-1) - }) - defer func() { - q.client.deferQuicConn(q.quicConn, err) - }() - q.client.udpInputMap.Delete(q.connId) - _ = q.inputConn.Close() - buf := pool.GetBuffer() - defer pool.PutBuffer(buf) - err = NewDissociate(q.connId).WriteTo(buf) - if err != nil { - return - } - stream, err := q.quicConn.OpenUniStream() - if err != nil { - return - } - _, err = buf.WriteTo(stream) - if err != nil { - return - } - err = stream.Close() - if err != nil { - return - } - return -} - -func (q *quicStreamPacketConn) SetDeadline(t time.Time) error { - //TODO implement me - return nil -} - -func (q *quicStreamPacketConn) SetReadDeadline(t time.Time) error { - return q.inputConn.SetReadDeadline(t) -} - -func (q *quicStreamPacketConn) SetWriteDeadline(t time.Time) error { - //TODO implement me - return nil -} - -func (q *quicStreamPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - packet, err := ReadPacket(q.inputConn) - if err != nil { - return - } - n = copy(p, packet.DATA) - addr = packet.ADDR.UDPAddr() - return -} - -func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - if len(p) > q.client.MaxUdpRelayPacketSize { - return 0, fmt.Errorf("udp packet too large(%d > %d)", len(p), q.client.MaxUdpRelayPacketSize) - } - if q.closed { - return 0, net.ErrClosed - } - defer func() { - q.client.deferQuicConn(q.quicConn, err) - }() - addr.String() - buf := pool.GetBuffer() - defer pool.PutBuffer(buf) - addrPort, err := netip.ParseAddrPort(addr.String()) - if err != nil { - return - } - err = NewPacket(q.connId, uint16(len(p)), NewAddressAddrPort(addrPort), p).WriteTo(buf) - if err != nil { - return - } - switch q.client.UdpRelayMode { - case "quic": - var stream quic.SendStream - stream, err = q.quicConn.OpenUniStream() - if err != nil { - return - } - defer stream.Close() - _, err = buf.WriteTo(stream) - if err != nil { - return - } - default: // native - err = q.quicConn.SendMessage(buf.Bytes()) - if err != nil { - return - } - } - n = len(p) - - return -} - -func (q *quicStreamPacketConn) LocalAddr() net.Addr { - return q.lAddr -} - -var _ net.PacketConn = &quicStreamPacketConn{} - func NewClient(clientOption *ClientOption, udp bool) *Client { c := &Client{ ClientOption: clientOption, @@ -558,5 +387,5 @@ func NewClient(clientOption *ClientOption, udp bool) *Client { } func closeClient(client *Client) { - client.Close(ClientClosed) + client.forceClose(ClientClosed, false) } diff --git a/transport/tuic/conn.go b/transport/tuic/conn.go new file mode 100644 index 00000000..4f914e26 --- /dev/null +++ b/transport/tuic/conn.go @@ -0,0 +1,250 @@ +package tuic + +import ( + "fmt" + "net" + "net/netip" + "sync" + "time" + + "github.com/metacubex/quic-go" + + N "github.com/Dreamacro/clash/common/net" + "github.com/Dreamacro/clash/common/pool" + "github.com/Dreamacro/clash/transport/tuic/congestion" +) + +func SetCongestionController(quicConn quic.Connection, cc string) { + switch cc { + case "cubic": + quicConn.SetCongestionControl( + congestion.NewCubicSender( + congestion.DefaultClock{}, + congestion.GetMaxPacketSize(quicConn.RemoteAddr()), + false, + nil, + ), + ) + case "new_reno": + quicConn.SetCongestionControl( + congestion.NewCubicSender( + congestion.DefaultClock{}, + congestion.GetMaxPacketSize(quicConn.RemoteAddr()), + true, + nil, + ), + ) + case "bbr": + quicConn.SetCongestionControl( + congestion.NewBBRSender( + congestion.DefaultClock{}, + congestion.GetMaxPacketSize(quicConn.RemoteAddr()), + congestion.InitialCongestionWindow, + congestion.DefaultBBRMaxCongestionWindow, + ), + ) + } +} + +type quicStreamConn struct { + quic.Stream + lock sync.Mutex + lAddr net.Addr + rAddr net.Addr + + ref any + + closeDeferFn func() + + closeOnce sync.Once + closeErr error +} + +func (q *quicStreamConn) Write(p []byte) (n int, err error) { + q.lock.Lock() + defer q.lock.Unlock() + return q.Stream.Write(p) +} + +func (q *quicStreamConn) Close() error { + q.closeOnce.Do(func() { + q.closeErr = q.close() + }) + return q.closeErr +} + +func (q *quicStreamConn) close() error { + if q.closeDeferFn != nil { + defer q.closeDeferFn() + } + + // https://github.com/cloudflare/cloudflared/commit/ed2bac026db46b239699ac5ce4fcf122d7cab2cd + // Make sure a possible writer does not block the lock forever. We need it, so we can close the writer + // side of the stream safely. + _ = q.Stream.SetWriteDeadline(time.Now()) + + // This lock is eventually acquired despite Write also acquiring it, because we set a deadline to writes. + q.lock.Lock() + defer q.lock.Unlock() + + // We have to clean up the receiving stream ourselves since the Close in the bottom does not handle that. + q.Stream.CancelRead(0) + return q.Stream.Close() +} + +func (q *quicStreamConn) LocalAddr() net.Addr { + return q.lAddr +} + +func (q *quicStreamConn) RemoteAddr() net.Addr { + return q.rAddr +} + +var _ net.Conn = &quicStreamConn{} + +type quicStreamPacketConn struct { + connId uint32 + quicConn quic.Connection + lAddr net.Addr + inputConn *N.BufferedConn + + udpRelayMode string + maxUdpRelayPacketSize int + + ref any + + deferQuicConnFn func(quicConn quic.Connection, err error) + closeDeferFn func() + + closeOnce sync.Once + closeErr error + closed bool +} + +func (q *quicStreamPacketConn) Close() error { + q.closeOnce.Do(func() { + q.closed = true + q.closeErr = q.close() + }) + return q.closeErr +} + +func (q *quicStreamPacketConn) close() (err error) { + if q.closeDeferFn != nil { + defer q.closeDeferFn() + } + defer func() { + if q.deferQuicConnFn != nil { + q.deferQuicConnFn(q.quicConn, err) + } + }() + if q.inputConn != nil { + _ = q.inputConn.Close() + q.inputConn = nil + + buf := pool.GetBuffer() + defer pool.PutBuffer(buf) + err = NewDissociate(q.connId).WriteTo(buf) + if err != nil { + return + } + var stream quic.SendStream + stream, err = q.quicConn.OpenUniStream() + if err != nil { + return + } + _, err = buf.WriteTo(stream) + if err != nil { + return + } + err = stream.Close() + if err != nil { + return + } + } + return +} + +func (q *quicStreamPacketConn) SetDeadline(t time.Time) error { + //TODO implement me + return nil +} + +func (q *quicStreamPacketConn) SetReadDeadline(t time.Time) error { + if q.inputConn != nil { + return q.inputConn.SetReadDeadline(t) + } + return nil +} + +func (q *quicStreamPacketConn) SetWriteDeadline(t time.Time) error { + //TODO implement me + return nil +} + +func (q *quicStreamPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + if q.inputConn != nil { + var packet Packet + packet, err = ReadPacket(q.inputConn) + if err != nil { + return + } + n = copy(p, packet.DATA) + addr = packet.ADDR.UDPAddr() + } else { + err = net.ErrClosed + } + return +} + +func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + if len(p) > q.maxUdpRelayPacketSize { + return 0, fmt.Errorf("udp packet too large(%d > %d)", len(p), q.maxUdpRelayPacketSize) + } + if q.closed { + return 0, net.ErrClosed + } + defer func() { + if q.deferQuicConnFn != nil { + q.deferQuicConnFn(q.quicConn, err) + } + }() + addr.String() + buf := pool.GetBuffer() + defer pool.PutBuffer(buf) + addrPort, err := netip.ParseAddrPort(addr.String()) + if err != nil { + return + } + err = NewPacket(q.connId, uint16(len(p)), NewAddressAddrPort(addrPort), p).WriteTo(buf) + if err != nil { + return + } + switch q.udpRelayMode { + case "quic": + var stream quic.SendStream + stream, err = q.quicConn.OpenUniStream() + if err != nil { + return + } + defer stream.Close() + _, err = buf.WriteTo(stream) + if err != nil { + return + } + default: // native + err = q.quicConn.SendMessage(buf.Bytes()) + if err != nil { + return + } + } + n = len(p) + + return +} + +func (q *quicStreamPacketConn) LocalAddr() net.Addr { + return q.lAddr +} + +var _ net.PacketConn = &quicStreamPacketConn{} diff --git a/transport/tuic/pool_client.go b/transport/tuic/pool_client.go index 19ebc4eb..d4e25157 100644 --- a/transport/tuic/pool_client.go +++ b/transport/tuic/pool_client.go @@ -75,7 +75,7 @@ func (t *PoolClient) dial(ctx context.Context, opts ...dialer.Option) (pc net.Pa return pc, addr, err } -func (t *PoolClient) Close() { +func (t *PoolClient) forceClose() { t.dialResultMutex.Lock() defer t.dialResultMutex.Unlock() for key := range t.dialResultMap { @@ -141,6 +141,7 @@ func (t *PoolClient) getClient(udp bool, opts ...dialer.Option) *Client { } } if client.openStreams.Load() == 0 && time.Now().Sub(client.lastVisited) > 30*time.Minute { + client.Close() next := it.Next() clients.Remove(it) it = next @@ -173,5 +174,5 @@ func NewClientPool(clientOption *ClientOption) *PoolClient { } func closeClientPool(client *PoolClient) { - client.Close() + client.forceClose() } diff --git a/transport/tuic/protocol.go b/transport/tuic/protocol.go index 98f7cd96..66a01e54 100644 --- a/transport/tuic/protocol.go +++ b/transport/tuic/protocol.go @@ -112,6 +112,29 @@ func NewAuthenticate(TKN [32]byte) Authenticate { } } +func ReadAuthenticateWithHead(head CommandHead, reader BufferedReader) (c Authenticate, err error) { + c.CommandHead = head + if err != nil { + return + } + if c.CommandHead.TYPE != AuthenticateType { + err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE) + } + _, err = io.ReadFull(reader, c.TKN[:]) + if err != nil { + return + } + return +} + +func ReadAuthenticate(reader BufferedReader) (c Authenticate, err error) { + head, err := ReadCommandHead(reader) + if err != nil { + return + } + return ReadAuthenticateWithHead(head, reader) +} + func GenTKN(token string) [32]byte { return blake3.Sum256([]byte(token)) } @@ -144,6 +167,29 @@ func NewConnect(ADDR Address) Connect { } } +func ReadConnectWithHead(head CommandHead, reader BufferedReader) (c Connect, err error) { + c.CommandHead = head + if err != nil { + return + } + if c.CommandHead.TYPE != ConnectType { + err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE) + } + c.ADDR, err = ReadAddress(reader) + if err != nil { + return + } + return +} + +func ReadConnect(reader BufferedReader) (c Connect, err error) { + head, err := ReadCommandHead(reader) + if err != nil { + return + } + return ReadConnectWithHead(head, reader) +} + func (c Connect) WriteTo(writer BufferedWriter) (err error) { err = c.CommandHead.WriteTo(writer) if err != nil { @@ -254,6 +300,29 @@ func NewDissociate(ASSOC_ID uint32) Dissociate { } } +func ReadDissociateWithHead(head CommandHead, reader BufferedReader) (c Dissociate, err error) { + c.CommandHead = head + if err != nil { + return + } + if c.CommandHead.TYPE != PacketType { + err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE) + } + err = binary.Read(reader, binary.BigEndian, &c.ASSOC_ID) + if err != nil { + return + } + return +} + +func ReadDissociate(reader BufferedReader) (c Dissociate, err error) { + head, err := ReadCommandHead(reader) + if err != nil { + return + } + return ReadDissociateWithHead(head, reader) +} + func (c Dissociate) WriteTo(writer BufferedWriter) (err error) { err = c.CommandHead.WriteTo(writer) if err != nil { @@ -308,6 +377,14 @@ func NewResponse(REP byte) Response { } } +func NewResponseSucceed() Response { + return NewResponse(0x00) +} + +func NewResponseFailed() Response { + return NewResponse(0xff) +} + func ReadResponseWithHead(head CommandHead, reader BufferedReader) (c Response, err error) { c.CommandHead = head if c.CommandHead.TYPE != ResponseType { @@ -466,6 +543,17 @@ func (c Address) WriteTo(writer BufferedWriter) (err error) { return } +func (c Address) String() string { + switch c.TYPE { + case AtypDomainName: + return net.JoinHostPort(string(c.ADDR[1:]), strconv.Itoa(int(c.PORT))) + default: + addr, _ := netip.AddrFromSlice(c.ADDR) + addrPort := netip.AddrPortFrom(addr, c.PORT) + return addrPort.String() + } +} + func (c Address) UDPAddr() *net.UDPAddr { return &net.UDPAddr{ IP: c.ADDR, diff --git a/transport/tuic/server.go b/transport/tuic/server.go new file mode 100644 index 00000000..4da86f9b --- /dev/null +++ b/transport/tuic/server.go @@ -0,0 +1,292 @@ +package tuic + +import ( + "bufio" + "bytes" + "context" + "crypto/tls" + "fmt" + "net" + "sync" + "time" + + "github.com/gofrs/uuid" + "github.com/metacubex/quic-go" + + N "github.com/Dreamacro/clash/common/net" + "github.com/Dreamacro/clash/common/pool" + C "github.com/Dreamacro/clash/constant" +) + +type ServerOption struct { + HandleTcpFn func(conn net.Conn, addr string) error + HandleUdpFn func(addr *net.UDPAddr, packet C.UDPPacket) error + + TlsConfig *tls.Config + QuicConfig *quic.Config + Tokens [][32]byte + CongestionController string + AuthenticationTimeout time.Duration + MaxUdpRelayPacketSize int +} + +type Server struct { + *ServerOption + listener quic.EarlyListener +} + +func NewServer(option *ServerOption, pc net.PacketConn) (*Server, error) { + listener, err := quic.ListenEarly(pc, option.TlsConfig, option.QuicConfig) + if err != nil { + return nil, err + } + return &Server{ + ServerOption: option, + listener: listener, + }, err +} + +func (s *Server) Serve() error { + for { + conn, err := s.listener.Accept(context.Background()) + if err != nil { + return err + } + uuid, err := uuid.NewV4() + if err != nil { + return err + } + h := &serverHandler{ + Server: s, + quicConn: conn, + uuid: uuid, + authCh: make(chan struct{}), + } + go h.handle() + } +} + +func (s *Server) Close() error { + return s.listener.Close() +} + +type serverHandler struct { + *Server + quicConn quic.Connection + uuid uuid.UUID + + authCh chan struct{} + authOk bool + authOnce sync.Once +} + +func (s *serverHandler) handle() { + time.AfterFunc(s.AuthenticationTimeout, func() { + s.authOnce.Do(func() { + _ = s.quicConn.CloseWithError(AuthenticationTimeout, "") + s.authOk = false + close(s.authCh) + }) + }) + go func() { + _ = s.handleUniStream() + }() + go func() { + _ = s.handleStream() + }() + go func() { + _ = s.handleMessage() + }() +} + +func (s *serverHandler) handleMessage() (err error) { + for { + var message []byte + message, err = s.quicConn.ReceiveMessage() + if err != nil { + return err + } + go func() (err error) { + buffer := bytes.NewBuffer(message) + packet, err := ReadPacket(buffer) + if err != nil { + return + } + return s.parsePacket(packet, "native") + }() + } +} + +func (s *serverHandler) parsePacket(packet Packet, udpRelayMode string) (err error) { + <-s.authCh + if !s.authOk { + return + } + var assocId uint32 + + assocId = packet.ASSOC_ID + pc := &quicStreamPacketConn{ + connId: assocId, + quicConn: s.quicConn, + lAddr: s.quicConn.LocalAddr(), + inputConn: nil, + udpRelayMode: udpRelayMode, + maxUdpRelayPacketSize: s.MaxUdpRelayPacketSize, + ref: s, + deferQuicConnFn: nil, + closeDeferFn: nil, + } + + return s.HandleUdpFn(packet.ADDR.UDPAddr(), &serverUDPPacket{ + pc: pc, + packet: &packet, + rAddr: s.genServerAssocIdAddr(assocId), + }) +} + +func (s *serverHandler) genServerAssocIdAddr(assocId uint32) net.Addr { + return ServerAssocIdAddr(fmt.Sprintf("tuic-%s-%d", s.uuid.String(), assocId)) +} + +func (s *serverHandler) handleStream() (err error) { + for { + var quicStream quic.Stream + quicStream, err = s.quicConn.AcceptStream(context.Background()) + if err != nil { + return err + } + + go func() (err error) { + stream := &quicStreamConn{ + Stream: quicStream, + lAddr: s.quicConn.LocalAddr(), + rAddr: s.quicConn.RemoteAddr(), + ref: s, + } + conn := N.NewBufferedConn(stream) + connect, err := ReadConnect(conn) + if err != nil { + return err + } + <-s.authCh + if !s.authOk { + return conn.Close() + } + + buf := pool.GetBuffer() + defer pool.PutBuffer(buf) + err = s.HandleTcpFn(conn, connect.ADDR.String()) + if err != nil { + err = NewResponseFailed().WriteTo(buf) + } + err = NewResponseSucceed().WriteTo(buf) + if err != nil { + _ = conn.Close() + return err + } + _, err = buf.WriteTo(stream) + if err != nil { + _ = conn.Close() + return err + } + + return + }() + } +} + +func (s *serverHandler) handleUniStream() (err error) { + for { + var stream quic.ReceiveStream + stream, err = s.quicConn.AcceptUniStream(context.Background()) + if err != nil { + return err + } + go func() (err error) { + defer func() { + stream.CancelRead(0) + }() + reader := bufio.NewReader(stream) + commandHead, err := ReadCommandHead(reader) + if err != nil { + return + } + switch commandHead.TYPE { + case AuthenticateType: + var authenticate Authenticate + authenticate, err = ReadAuthenticateWithHead(commandHead, reader) + if err != nil { + return + } + ok := false + for _, tkn := range s.Tokens { + if authenticate.TKN == tkn { + ok = true + break + } + } + s.authOnce.Do(func() { + if !ok { + _ = s.quicConn.CloseWithError(AuthenticationFailed, "") + } + s.authOk = ok + close(s.authCh) + }) + case PacketType: + var packet Packet + packet, err = ReadPacketWithHead(commandHead, reader) + if err != nil { + return + } + return s.parsePacket(packet, "quic") + case DissociateType: + var disassociate Dissociate + disassociate, err = ReadDissociateWithHead(commandHead, reader) + if err != nil { + return + } + disassociate.BytesLen() + } + return + }() + } +} + +type ServerAssocIdAddr string + +func (a ServerAssocIdAddr) Network() string { + return "ServerAssocIdAddr" +} + +func (a ServerAssocIdAddr) String() string { + return string(a) +} + +type serverUDPPacket struct { + pc *quicStreamPacketConn + packet *Packet + rAddr net.Addr +} + +func (s *serverUDPPacket) InAddr() net.Addr { + return s.pc.LocalAddr() +} + +func (s *serverUDPPacket) LocalAddr() net.Addr { + return s.rAddr +} + +func (s *serverUDPPacket) Data() []byte { + return s.packet.DATA +} + +func (s *serverUDPPacket) WriteBack(b []byte, addr net.Addr) (n int, err error) { + return s.pc.WriteTo(b, addr) +} + +func (s *serverUDPPacket) Drop() { + s.packet.DATA = nil +} + +var _ C.UDPPacket = &serverUDPPacket{} +var _ C.UDPPacketInAddr = &serverUDPPacket{} From 562819e3ca17c4c4ca0753a37f34aa630c6f1654 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 28 Nov 2022 18:18:51 +0800 Subject: [PATCH 171/250] chore: tuic-server support disassociate command --- listener/tuic/server.go | 8 ++++---- transport/tuic/conn.go | 22 ++++++++++++++-------- transport/tuic/protocol.go | 15 +++++++++++++++ transport/tuic/server.go | 25 ++++++++++++++++++++----- 4 files changed, 53 insertions(+), 17 deletions(-) diff --git a/listener/tuic/server.go b/listener/tuic/server.go index 3ea2dd80..870c9f80 100644 --- a/listener/tuic/server.go +++ b/listener/tuic/server.go @@ -60,13 +60,13 @@ func New(config config.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- *inb } option := &tuic.ServerOption{ - HandleTcpFn: func(conn net.Conn, addr string) error { - tcpIn <- inbound.NewSocket(socks5.ParseAddr(addr), conn, C.TUIC) + HandleTcpFn: func(conn net.Conn, addr socks5.Addr) error { + tcpIn <- inbound.NewSocket(addr, conn, C.TUIC) return nil }, - HandleUdpFn: func(addr *net.UDPAddr, packet C.UDPPacket) error { + HandleUdpFn: func(addr socks5.Addr, packet C.UDPPacket) error { select { - case udpIn <- inbound.NewPacket(socks5.ParseAddrToSocksAddr(addr), packet, C.TUIC): + case udpIn <- inbound.NewPacket(addr, packet, C.TUIC): default: } return nil diff --git a/transport/tuic/conn.go b/transport/tuic/conn.go index 4f914e26..b030c3d8 100644 --- a/transport/tuic/conn.go +++ b/transport/tuic/conn.go @@ -5,6 +5,7 @@ import ( "net" "net/netip" "sync" + "sync/atomic" "time" "github.com/metacubex/quic-go" @@ -115,6 +116,7 @@ type quicStreamPacketConn struct { deferQuicConnFn func(quicConn quic.Connection, err error) closeDeferFn func() + writeClosed *atomic.Bool closeOnce sync.Once closeErr error @@ -133,11 +135,11 @@ func (q *quicStreamPacketConn) close() (err error) { if q.closeDeferFn != nil { defer q.closeDeferFn() } - defer func() { - if q.deferQuicConnFn != nil { + if q.deferQuicConnFn != nil { + defer func() { q.deferQuicConnFn(q.quicConn, err) - } - }() + }() + } if q.inputConn != nil { _ = q.inputConn.Close() q.inputConn = nil @@ -204,11 +206,15 @@ func (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err erro if q.closed { return 0, net.ErrClosed } - defer func() { - if q.deferQuicConnFn != nil { + if q.writeClosed != nil && q.writeClosed.Load() { + _ = q.Close() + return 0, net.ErrClosed + } + if q.deferQuicConnFn != nil { + defer func() { q.deferQuicConnFn(q.quicConn, err) - } - }() + }() + } addr.String() buf := pool.GetBuffer() defer pool.PutBuffer(buf) diff --git a/transport/tuic/protocol.go b/transport/tuic/protocol.go index 66a01e54..f7456e95 100644 --- a/transport/tuic/protocol.go +++ b/transport/tuic/protocol.go @@ -554,6 +554,21 @@ func (c Address) String() string { } } +func (c Address) SocksAddr() socks5.Addr { + addr := make([]byte, 1+len(c.ADDR)+2) + switch c.TYPE { + case AtypIPv4: + addr[0] = socks5.AtypIPv4 + case AtypIPv6: + addr[0] = socks5.AtypIPv6 + case AtypDomainName: + addr[0] = socks5.AtypDomainName + } + copy(addr[1:], c.ADDR) + binary.BigEndian.PutUint16(addr[len(addr)-2:], c.PORT) + return addr +} + func (c Address) UDPAddr() *net.UDPAddr { return &net.UDPAddr{ IP: c.ADDR, diff --git a/transport/tuic/server.go b/transport/tuic/server.go index 4da86f9b..07b4c6c1 100644 --- a/transport/tuic/server.go +++ b/transport/tuic/server.go @@ -8,6 +8,7 @@ import ( "fmt" "net" "sync" + "sync/atomic" "time" "github.com/gofrs/uuid" @@ -16,11 +17,12 @@ import ( N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/pool" C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/transport/socks5" ) type ServerOption struct { - HandleTcpFn func(conn net.Conn, addr string) error - HandleUdpFn func(addr *net.UDPAddr, packet C.UDPPacket) error + HandleTcpFn func(conn net.Conn, addr socks5.Addr) error + HandleUdpFn func(addr socks5.Addr, packet C.UDPPacket) error TlsConfig *tls.Config QuicConfig *quic.Config @@ -78,6 +80,8 @@ type serverHandler struct { authCh chan struct{} authOk bool authOnce sync.Once + + udpInputMap sync.Map } func (s *serverHandler) handle() { @@ -125,6 +129,13 @@ func (s *serverHandler) parsePacket(packet Packet, udpRelayMode string) (err err var assocId uint32 assocId = packet.ASSOC_ID + + v, _ := s.udpInputMap.LoadOrStore(assocId, &atomic.Bool{}) + writeClosed := v.(*atomic.Bool) + if writeClosed.Load() { + return nil + } + pc := &quicStreamPacketConn{ connId: assocId, quicConn: s.quicConn, @@ -135,9 +146,10 @@ func (s *serverHandler) parsePacket(packet Packet, udpRelayMode string) (err err ref: s, deferQuicConnFn: nil, closeDeferFn: nil, + writeClosed: writeClosed, } - return s.HandleUdpFn(packet.ADDR.UDPAddr(), &serverUDPPacket{ + return s.HandleUdpFn(packet.ADDR.SocksAddr(), &serverUDPPacket{ pc: pc, packet: &packet, rAddr: s.genServerAssocIdAddr(assocId), @@ -175,7 +187,7 @@ func (s *serverHandler) handleStream() (err error) { buf := pool.GetBuffer() defer pool.PutBuffer(buf) - err = s.HandleTcpFn(conn, connect.ADDR.String()) + err = s.HandleTcpFn(conn, connect.ADDR.SocksAddr()) if err != nil { err = NewResponseFailed().WriteTo(buf) } @@ -245,7 +257,10 @@ func (s *serverHandler) handleUniStream() (err error) { if err != nil { return } - disassociate.BytesLen() + if v, loaded := s.udpInputMap.LoadAndDelete(disassociate.ASSOC_ID); loaded { + writeClosed := v.(*atomic.Bool) + writeClosed.Store(true) + } } return }() From 4b1d4a3e20a09d3dcaed5492a29318ba62b907a1 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 28 Nov 2022 18:28:22 +0800 Subject: [PATCH 172/250] chore: tuic-server support heartbeat command --- transport/tuic/protocol.go | 4 ++-- transport/tuic/server.go | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/transport/tuic/protocol.go b/transport/tuic/protocol.go index f7456e95..f7b85a23 100644 --- a/transport/tuic/protocol.go +++ b/transport/tuic/protocol.go @@ -349,7 +349,7 @@ func NewHeartbeat() Heartbeat { } } -func ReadHeartbeatWithHead(head CommandHead, reader BufferedReader) (c Response, err error) { +func ReadHeartbeatWithHead(head CommandHead, reader BufferedReader) (c Heartbeat, err error) { c.CommandHead = head if c.CommandHead.TYPE != HeartbeatType { err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE) @@ -357,7 +357,7 @@ func ReadHeartbeatWithHead(head CommandHead, reader BufferedReader) (c Response, return } -func ReadHeartbeat(reader BufferedReader) (c Response, err error) { +func ReadHeartbeat(reader BufferedReader) (c Heartbeat, err error) { head, err := ReadCommandHead(reader) if err != nil { return diff --git a/transport/tuic/server.go b/transport/tuic/server.go index 07b4c6c1..78344589 100644 --- a/transport/tuic/server.go +++ b/transport/tuic/server.go @@ -261,6 +261,13 @@ func (s *serverHandler) handleUniStream() (err error) { writeClosed := v.(*atomic.Bool) writeClosed.Store(true) } + case HeartbeatType: + var heartbeat Heartbeat + heartbeat, err = ReadHeartbeatWithHead(commandHead, reader) + if err != nil { + return + } + heartbeat.BytesLen() } return }() From 01e382285d49aa12c76f1bddc2298e443ed7f5c1 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 28 Nov 2022 18:53:09 +0800 Subject: [PATCH 173/250] chore: tuic-server support restful api patch --- hub/executor/executor.go | 1 + hub/route/configs.go | 53 +++++++++++++++++++++++++++++++++++++--- listener/listener.go | 15 ++++++++++-- 3 files changed, 63 insertions(+), 6 deletions(-) diff --git a/hub/executor/executor.go b/hub/executor/executor.go index dfd3a75d..0dece55c 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -124,6 +124,7 @@ func GetGeneral() *config.General { IPv6: !resolver.DisableIPv6, GeodataLoader: G.LoaderName(), Tun: P.GetTunConf(), + TuicServer: P.GetTuicConf(), Interface: dialer.DefaultInterface.Load(), Sniffing: tunnel.IsSniffing(), TCPConcurrent: dialer.GetDial(), diff --git a/hub/route/configs.go b/hub/route/configs.go index 3563555c..cf0a6a9c 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -41,7 +41,7 @@ type configSchema struct { TProxyPort *int `json:"tproxy-port"` MixedPort *int `json:"mixed-port"` Tun *tunSchema `json:"tun"` - TuicServer *config.TuicServer `json:"tuic-server"` + TuicServer *tuicServerSchema `json:"tuic-server"` ShadowSocksConfig *string `json:"ss-config"` VmessConfig *string `json:"vmess-config"` TcptunConfig *string `json:"tcptun-config"` @@ -82,6 +82,19 @@ type tunSchema struct { UDPTimeout *int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"` } +type tuicServerSchema struct { + Enable bool `yaml:"enable" json:"enable"` + Listen *string `yaml:"listen" json:"listen"` + Token *[]string `yaml:"token" json:"token"` + Certificate *string `yaml:"certificate" json:"certificate"` + PrivateKey *string `yaml:"private-key" json:"private-key"` + CongestionController *string `yaml:"congestion-controller" json:"congestion-controller,omitempty"` + MaxIdleTime *int `yaml:"max-idle-time" json:"max-idle-time,omitempty"` + AuthenticationTimeout *int `yaml:"authentication-timeout" json:"authentication-timeout,omitempty"` + ALPN *[]string `yaml:"alpn" json:"alpn,omitempty"` + MaxUdpRelayPacketSize *int `yaml:"max-udp-relay-packet-size" json:"max-udp-relay-packet-size,omitempty"` +} + func getConfigs(w http.ResponseWriter, r *http.Request) { general := executor.GetGeneral() render.JSON(w, r, general) @@ -161,6 +174,40 @@ func pointerOrDefaultTun(p *tunSchema, def config.Tun) config.Tun { return def } +func pointerOrDefaultTuicServer(p *tuicServerSchema, def config.TuicServer) config.TuicServer { + if p != nil { + def.Enable = p.Enable + if p.Listen != nil { + def.Listen = *p.Listen + } + if p.Token != nil { + def.Token = *p.Token + } + if p.Certificate != nil { + def.Certificate = *p.Certificate + } + if p.PrivateKey != nil { + def.PrivateKey = *p.PrivateKey + } + if p.CongestionController != nil { + def.CongestionController = *p.CongestionController + } + if p.MaxIdleTime != nil { + def.MaxIdleTime = *p.MaxIdleTime + } + if p.AuthenticationTimeout != nil { + def.AuthenticationTimeout = *p.AuthenticationTimeout + } + if p.ALPN != nil { + def.ALPN = *p.ALPN + } + if p.MaxUdpRelayPacketSize != nil { + def.MaxUdpRelayPacketSize = *p.MaxUdpRelayPacketSize + } + } + return def +} + func patchConfigs(w http.ResponseWriter, r *http.Request) { general := &configSchema{} if err := render.DecodeJSON(r.Body, general); err != nil { @@ -204,9 +251,7 @@ func patchConfigs(w http.ResponseWriter, r *http.Request) { P.ReCreateVmess(pointerOrDefaultString(general.VmessConfig, ports.VmessConfig), tcpIn, udpIn) P.ReCreateTcpTun(pointerOrDefaultString(general.TcptunConfig, ports.TcpTunConfig), tcpIn, udpIn) P.ReCreateUdpTun(pointerOrDefaultString(general.UdptunConfig, ports.UdpTunConfig), tcpIn, udpIn) - if general.TuicServer != nil { - P.ReCreateTuic(*general.TuicServer, tcpIn, udpIn) - } + P.ReCreateTuic(pointerOrDefaultTuicServer(general.TuicServer, P.LastTuicConf), tcpIn, udpIn) if general.Mode != nil { tunnel.SetMode(*general.Mode) diff --git a/listener/listener.go b/listener/listener.go index ff8fb37c..ccda571e 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -64,7 +64,8 @@ var ( autoRedirMux sync.Mutex tcMux sync.Mutex - LastTunConf config.Tun + LastTunConf config.Tun + LastTuicConf config.TuicServer ) type Ports struct { @@ -88,6 +89,13 @@ func GetTunConf() config.Tun { return tunLister.Config() } +func GetTuicConf() config.TuicServer { + if tuicListener == nil { + return config.TuicServer{Enable: false} + } + return tuicListener.Config() +} + func AllowLan() bool { return allowLan } @@ -395,7 +403,10 @@ func ReCreateUdpTun(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inb func ReCreateTuic(config config.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { tuicMux.Lock() - defer tuicMux.Unlock() + defer func() { + LastTuicConf = config + tuicMux.Unlock() + }() shouldIgnore := false var err error From dd6f7e3701ba4c9133494d7bda5152f6a9f6365a Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 28 Nov 2022 19:11:55 +0800 Subject: [PATCH 174/250] fix: structure decode first do strict match --- adapter/outbound/tuic.go | 8 ++++---- common/structure/structure.go | 14 +++++++------- listener/tuic/server.go | 13 ++++--------- transport/tuic/conn.go | 5 +++++ 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index 97938014..264c991b 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -169,12 +169,12 @@ func NewTuic(option TuicOption) (*Tuic, error) { EnableDatagrams: true, } if option.ReceiveWindowConn == 0 { - quicConfig.InitialStreamReceiveWindow = DefaultStreamReceiveWindow / 10 - quicConfig.MaxStreamReceiveWindow = DefaultStreamReceiveWindow + quicConfig.InitialStreamReceiveWindow = tuic.DefaultStreamReceiveWindow / 10 + quicConfig.MaxStreamReceiveWindow = tuic.DefaultStreamReceiveWindow } if option.ReceiveWindow == 0 { - quicConfig.InitialConnectionReceiveWindow = DefaultConnectionReceiveWindow / 10 - quicConfig.MaxConnectionReceiveWindow = DefaultConnectionReceiveWindow + quicConfig.InitialConnectionReceiveWindow = tuic.DefaultConnectionReceiveWindow / 10 + quicConfig.MaxConnectionReceiveWindow = tuic.DefaultConnectionReceiveWindow } if len(option.Ip) > 0 { diff --git a/common/structure/structure.go b/common/structure/structure.go index 2b5e024f..958ad54b 100644 --- a/common/structure/structure.go +++ b/common/structure/structure.go @@ -49,12 +49,12 @@ func (d *Decoder) Decode(src map[string]any, dst any) error { key, omitKey, found := strings.Cut(tag, ",") omitempty := found && omitKey == "omitempty" - if d.option.KeyReplacer != nil { - key = d.option.KeyReplacer.Replace(key) - } - value, ok := src[key] if !ok { + if d.option.KeyReplacer != nil { + key = d.option.KeyReplacer.Replace(key) + } + for _strKey := range src { strKey := _strKey if d.option.KeyReplacer != nil { @@ -364,9 +364,6 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e tagValue = strings.SplitN(tagValue, ",", 2)[0] if tagValue != "" { fieldName = tagValue - if d.option.KeyReplacer != nil { - fieldName = d.option.KeyReplacer.Replace(fieldName) - } } rawMapKey := reflect.ValueOf(fieldName) @@ -374,6 +371,9 @@ func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) e if !rawMapVal.IsValid() { // Do a slower search by iterating over each key and // doing case-insensitive search. + if d.option.KeyReplacer != nil { + fieldName = d.option.KeyReplacer.Replace(fieldName) + } for dataValKey := range dataValKeys { mK, ok := dataValKey.Interface().(string) if !ok { diff --git a/listener/tuic/server.go b/listener/tuic/server.go index 870c9f80..64ec8489 100644 --- a/listener/tuic/server.go +++ b/listener/tuic/server.go @@ -17,11 +17,6 @@ import ( "github.com/Dreamacro/clash/transport/tuic" ) -const ( - DefaultStreamReceiveWindow = 15728640 // 15 MB/s - DefaultConnectionReceiveWindow = 67108864 // 64 MB/s -) - type Listener struct { closed bool config config.TuicServer @@ -49,10 +44,10 @@ func New(config config.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- *inb MaxIncomingUniStreams: 1 >> 32, EnableDatagrams: true, } - quicConfig.InitialStreamReceiveWindow = DefaultStreamReceiveWindow / 10 - quicConfig.MaxStreamReceiveWindow = DefaultStreamReceiveWindow - quicConfig.InitialConnectionReceiveWindow = DefaultConnectionReceiveWindow / 10 - quicConfig.MaxConnectionReceiveWindow = DefaultConnectionReceiveWindow + quicConfig.InitialStreamReceiveWindow = tuic.DefaultStreamReceiveWindow / 10 + quicConfig.MaxStreamReceiveWindow = tuic.DefaultStreamReceiveWindow + quicConfig.InitialConnectionReceiveWindow = tuic.DefaultConnectionReceiveWindow / 10 + quicConfig.MaxConnectionReceiveWindow = tuic.DefaultConnectionReceiveWindow tokens := make([][32]byte, len(config.Token)) for i, token := range config.Token { diff --git a/transport/tuic/conn.go b/transport/tuic/conn.go index b030c3d8..3e759f25 100644 --- a/transport/tuic/conn.go +++ b/transport/tuic/conn.go @@ -15,6 +15,11 @@ import ( "github.com/Dreamacro/clash/transport/tuic/congestion" ) +const ( + DefaultStreamReceiveWindow = 15728640 // 15 MB/s + DefaultConnectionReceiveWindow = 67108864 // 64 MB/s +) + func SetCongestionController(quicConn quic.Connection, cc string) { switch cc { case "cubic": From bc94c5078306a4e229bad77603c9063e3ecef6e0 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 28 Nov 2022 20:09:24 +0800 Subject: [PATCH 175/250] chore: update dependencies --- go.mod | 6 +++--- go.sum | 14 +++++++------- test/go.mod | 4 ++-- test/go.sum | 10 +++++----- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/go.mod b/go.mod index 3dd88a34..9502b144 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/miekg/dns v1.1.50 github.com/oschwald/geoip2-golang v1.8.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 - github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4 + github.com/sagernet/sing v0.1.0 github.com/sagernet/sing-shadowsocks v0.0.0-20221112030934-e55284e180ea github.com/sagernet/sing-tun v0.0.0-20221104121441-66c48a57776f github.com/sagernet/sing-vmess v0.0.0-20221109021549-b446d5bdddf0 @@ -46,14 +46,14 @@ require ( lukechampine.com/blake3 v1.1.7 ) -replace github.com/sagernet/sing-tun => github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc +replace github.com/sagernet/sing-tun => github.com/MetaCubeX/sing-tun v0.0.0-20221127052020-dbf40f3160d8 replace github.com/sagernet/sing-shadowsocks => github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221116103607-48a7095182b1 require ( github.com/ajg/form v1.5.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/golang/mock v1.6.0 // indirect github.com/google/btree v1.0.1 // indirect diff --git a/go.sum b/go.sum index e4a14291..9fb2c366 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221116103607-48a7095182b1 h1:guMcwJSIjk+jg/38uMPK5hIWVSaLHJ/l+ABZ8w2CKm0= github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221116103607-48a7095182b1/go.mod h1:3bW+hVWFXOxRC1HL6CO6QHkegqjLohErGbcvt6dUN18= -github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc h1:B1vmR4cwDjJQ6YM8NkuXmt9vIYOdV/+qA+F3UAFE3YY= -github.com/MetaCubeX/sing-tun v0.0.0-20221105124245-542e9b56a6dc/go.mod h1:1u3pjXA9HmH7kRiBJqM3C/zPxrxnCLd3svmqtub/RFU= +github.com/MetaCubeX/sing-tun v0.0.0-20221127052020-dbf40f3160d8 h1:YufhIKD2ecIFv+UZ8yTnmjYhwnrLkIGBssPgAfNmqvE= +github.com/MetaCubeX/sing-tun v0.0.0-20221127052020-dbf40f3160d8/go.mod h1:btUIxuI5vfzUcEDFVrG48aHM2stzg4jcI7mFQeEsei4= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= @@ -22,8 +22,8 @@ github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8= github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= @@ -119,8 +119,8 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= -github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4 h1:LO7xMvMGhYmjQg2vjhTzsODyzs9/WLYu5Per+/8jIeo= -github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4= +github.com/sagernet/sing v0.1.0 h1:FGmaP2BVPYO2IyC/3R1DaQa/zr+kOKHRgWqrmOF+Gu8= +github.com/sagernet/sing v0.1.0/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4= github.com/sagernet/sing-vmess v0.0.0-20221109021549-b446d5bdddf0 h1:z3kuD3hPNdEq7/wVy5lwE21f+8ZTazBtR81qswxJoCc= github.com/sagernet/sing-vmess v0.0.0-20221109021549-b446d5bdddf0/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM= github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c h1:qP3ZOHnjZalvqbjundbXiv/YrNlo3HOgrKc+S1QGs0U= @@ -206,9 +206,9 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.1-0.20221110211117-d684c6f88669 h1:pvmSpBoSG0gD2LLPAX15QHPig8xsbU0tu1sSAmResqk= golang.org/x/sys v0.2.1-0.20221110211117-d684c6f88669/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/test/go.mod b/test/go.mod index 85fd0c6a..8be96364 100644 --- a/test/go.mod +++ b/test/go.mod @@ -23,7 +23,7 @@ require ( github.com/dlclark/regexp2 v1.7.0 // indirect github.com/docker/distribution v2.8.1+incompatible // indirect github.com/docker/go-units v0.4.0 // indirect - github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/gofrs/uuid v4.3.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect @@ -57,7 +57,7 @@ require ( github.com/sagernet/abx-go v0.0.0-20220819185957-dba1257d738e // 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.0.0-20221008120626-60a9910eefe4 // indirect + github.com/sagernet/sing v0.1.0 // indirect github.com/sagernet/sing-shadowsocks v0.0.0-20221112030934-e55284e180ea // indirect github.com/sagernet/sing-tun v0.0.0-20221104121441-66c48a57776f // indirect github.com/sagernet/sing-vmess v0.0.0-20221109021549-b446d5bdddf0 // indirect diff --git a/test/go.sum b/test/go.sum index 122ab0ee..96d6f3ab 100644 --- a/test/go.sum +++ b/test/go.sum @@ -27,8 +27,8 @@ github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc= github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gofrs/uuid v4.3.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc= @@ -129,8 +129,8 @@ github.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE= github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= -github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4 h1:LO7xMvMGhYmjQg2vjhTzsODyzs9/WLYu5Per+/8jIeo= -github.com/sagernet/sing v0.0.0-20221008120626-60a9910eefe4/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4= +github.com/sagernet/sing v0.1.0 h1:FGmaP2BVPYO2IyC/3R1DaQa/zr+kOKHRgWqrmOF+Gu8= +github.com/sagernet/sing v0.1.0/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4= github.com/sagernet/sing-shadowsocks v0.0.0-20221112030934-e55284e180ea h1:OO+oV7XNQ7eah2bQyT2pSAoeCBHpbAU3cVVbGd7TE/g= github.com/sagernet/sing-shadowsocks v0.0.0-20221112030934-e55284e180ea/go.mod h1:16sNARQbsFbYIzAuPySszQA6Wfgzk7GWSzh1a6kDrUU= github.com/sagernet/sing-tun v0.0.0-20221104121441-66c48a57776f h1:CXF+nErOb9f7qiHingSgTa2/lJAgmEFtAQ47oVwdRGU= @@ -231,9 +231,9 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.1-0.20221110211117-d684c6f88669 h1:pvmSpBoSG0gD2LLPAX15QHPig8xsbU0tu1sSAmResqk= golang.org/x/sys v0.2.1-0.20221110211117-d684c6f88669/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= From e715ccbdd5e9192d8eae6c6897a4a59f03826e61 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 28 Nov 2022 20:12:09 +0800 Subject: [PATCH 176/250] chore: update docs --- docs/config.yaml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/config.yaml b/docs/config.yaml index f8d06e91..78e9f025 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -97,11 +97,9 @@ sniffer: - "443" # - 8000-9999 -# shadowsocks,vmess,tcp/udp端口转发 入口配置(传入流量将和socks,mixed等入口一样按照mode所指定的方式进行匹配处理) +# shadowsocks,vmess 入口配置(传入流量将和socks,mixed等入口一样按照mode所指定的方式进行匹配处理) # ss-config: ss://2022-blake3-aes-256-gcm:vlmpIPSyHH6f4S8WVPdRIHIlzmB+GIRfoH3aNJ/t9Gg=@:23456 # vmess-config: vmess://1:9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68@:12345 -# tcptun-config: 127.0.0.1:801=www.example.com:80,127.0.0.1:4431=www.example.com:443 -# udptun-config: 127.0.0.1:531=8.8.8.8:53,127.0.0.1:532=1.1.1.1:53 # tuic服务器入口(传入流量将和socks,mixed等入口一样按照mode所指定的方式进行匹配处理) #tuic-server: @@ -118,6 +116,16 @@ sniffer: # - h3 # max-udp-relay-packet-size: 1500 +tunnels: + # one line config + - tcp/udp,127.0.0.1:6553,114.114.114.114:53,proxy + - tcp,127.0.0.1:6666,rds.mysql.com:3306,vpn + # full yaml config + - network: [tcp, udp] + address: 127.0.0.1:7777 + target: target.com + proxy: proxy + profile: # 存储select选择记录 store-selected: false From db81db5363607870556d4ae8c7442817bcb5bc8c Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 28 Nov 2022 20:31:32 +0800 Subject: [PATCH 177/250] Chore: allow tunnels don't set special proxy --- config/config.go | 12 ++++++++---- listener/tunnel/tcp.go | 3 --- listener/tunnel/udp.go | 3 --- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/config/config.go b/config/config.go index 3f57a0a1..9033f162 100644 --- a/config/config.go +++ b/config/config.go @@ -340,7 +340,7 @@ func (t *Tunnel) UnmarshalYAML(unmarshal func(any) error) error { parts := lo.Map(strings.Split(tp, ","), func(s string, _ int) string { return strings.TrimSpace(s) }) - if len(parts) != 4 { + if len(parts) != 3 && len(parts) != 4 { return fmt.Errorf("invalid tunnel config %s", tp) } network := strings.Split(parts[0], "/") @@ -367,8 +367,10 @@ func (t *Tunnel) UnmarshalYAML(unmarshal func(any) error) error { Network: network, Address: address, Target: target, - Proxy: parts[3], }) + if len(parts) == 4 { + t.Proxy = parts[3] + } return nil } @@ -620,8 +622,10 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { config.Tunnels = rawCfg.Tunnels // verify tunnels for _, t := range config.Tunnels { - if _, ok := config.Proxies[t.Proxy]; !ok { - return nil, fmt.Errorf("tunnel proxy %s not found", t.Proxy) + if len(t.Proxy) > 0 { + if _, ok := config.Proxies[t.Proxy]; !ok { + return nil, fmt.Errorf("tunnel proxy %s not found", t.Proxy) + } } } diff --git a/listener/tunnel/tcp.go b/listener/tunnel/tcp.go index 58949550..4ae5865c 100644 --- a/listener/tunnel/tcp.go +++ b/listener/tunnel/tcp.go @@ -6,7 +6,6 @@ import ( "github.com/Dreamacro/clash/adapter/inbound" C "github.com/Dreamacro/clash/constant" - "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/transport/socks5" ) @@ -52,8 +51,6 @@ func New(addr, target, proxy string, in chan<- C.ConnContext) (*Listener, error) return nil, fmt.Errorf("invalid target address %s", target) } - log.Infoln("TCP tunnel %s <-> %s", l.Addr().String(), target) - rl := &Listener{ listener: l, target: targetAddr, diff --git a/listener/tunnel/udp.go b/listener/tunnel/udp.go index 22e194a9..ee0ecbaf 100644 --- a/listener/tunnel/udp.go +++ b/listener/tunnel/udp.go @@ -7,7 +7,6 @@ import ( "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/common/pool" C "github.com/Dreamacro/clash/constant" - "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/transport/socks5" ) @@ -46,8 +45,6 @@ func NewUDP(addr, target, proxy string, in chan<- *inbound.PacketAdapter) (*Pack return nil, fmt.Errorf("invalid target address %s", target) } - log.Infoln("Udp tunnel %s <-> %s", l.LocalAddr().String(), target) - sl := &PacketConn{ conn: l, target: targetAddr, From 9ea09b2b9441f942d8126c1d7d2fc2cfd9225a54 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 29 Nov 2022 00:42:26 +0800 Subject: [PATCH 178/250] fix: tuic protocol error --- transport/tuic/protocol.go | 10 ++++++++-- transport/tuic/server.go | 4 +++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/transport/tuic/protocol.go b/transport/tuic/protocol.go index f7b85a23..ab696e79 100644 --- a/transport/tuic/protocol.go +++ b/transport/tuic/protocol.go @@ -33,7 +33,7 @@ const ( PacketType = CommandType(0x02) DissociateType = CommandType(0x03) HeartbeatType = CommandType(0x04) - ResponseType = CommandType(0x05) + ResponseType = CommandType(0xff) ) func (c CommandType) String() string { @@ -119,6 +119,7 @@ func ReadAuthenticateWithHead(head CommandHead, reader BufferedReader) (c Authen } if c.CommandHead.TYPE != AuthenticateType { err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE) + return } _, err = io.ReadFull(reader, c.TKN[:]) if err != nil { @@ -174,6 +175,7 @@ func ReadConnectWithHead(head CommandHead, reader BufferedReader) (c Connect, er } if c.CommandHead.TYPE != ConnectType { err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE) + return } c.ADDR, err = ReadAddress(reader) if err != nil { @@ -231,6 +233,7 @@ func ReadPacketWithHead(head CommandHead, reader BufferedReader) (c Packet, err } if c.CommandHead.TYPE != PacketType { err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE) + return } err = binary.Read(reader, binary.BigEndian, &c.ASSOC_ID) if err != nil { @@ -305,8 +308,9 @@ func ReadDissociateWithHead(head CommandHead, reader BufferedReader) (c Dissocia if err != nil { return } - if c.CommandHead.TYPE != PacketType { + if c.CommandHead.TYPE != DissociateType { err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE) + return } err = binary.Read(reader, binary.BigEndian, &c.ASSOC_ID) if err != nil { @@ -353,6 +357,7 @@ func ReadHeartbeatWithHead(head CommandHead, reader BufferedReader) (c Heartbeat c.CommandHead = head if c.CommandHead.TYPE != HeartbeatType { err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE) + return } return } @@ -389,6 +394,7 @@ func ReadResponseWithHead(head CommandHead, reader BufferedReader) (c Response, c.CommandHead = head if c.CommandHead.TYPE != ResponseType { err = fmt.Errorf("error command type: %s", c.CommandHead.TYPE) + return } c.REP, err = reader.ReadByte() if err != nil { diff --git a/transport/tuic/server.go b/transport/tuic/server.go index 78344589..c1213d68 100644 --- a/transport/tuic/server.go +++ b/transport/tuic/server.go @@ -190,8 +190,10 @@ func (s *serverHandler) handleStream() (err error) { err = s.HandleTcpFn(conn, connect.ADDR.SocksAddr()) if err != nil { err = NewResponseFailed().WriteTo(buf) + defer conn.Close() + } else { + err = NewResponseSucceed().WriteTo(buf) } - err = NewResponseSucceed().WriteTo(buf) if err != nil { _ = conn.Close() return err From 8c0fbb3665364befcefc2dd72f2177fe54767085 Mon Sep 17 00:00:00 2001 From: metacubex Date: Mon, 28 Nov 2022 19:52:55 +0800 Subject: [PATCH 179/250] chore: restful api display fast-open for tuic and hysteria --- adapter/adapter.go | 1 + adapter/outbound/base.go | 8 ++++++++ adapter/outbound/hysteria.go | 5 ++--- adapter/outbound/tuic.go | 1 + constant/adapters.go | 1 + 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/adapter/adapter.go b/adapter/adapter.go index aa860ed4..feef72be 100644 --- a/adapter/adapter.go +++ b/adapter/adapter.go @@ -92,6 +92,7 @@ func (p *Proxy) MarshalJSON() ([]byte, error) { mapping["history"] = p.DelayHistory() mapping["name"] = p.Name() mapping["udp"] = p.SupportUDP() + mapping["tfo"] = p.SupportTFO() return json.Marshal(mapping) } diff --git a/adapter/outbound/base.go b/adapter/outbound/base.go index 145a3f97..a7e068dd 100644 --- a/adapter/outbound/base.go +++ b/adapter/outbound/base.go @@ -18,6 +18,7 @@ type Base struct { iface string tp C.AdapterType udp bool + tfo bool rmark int id string prefer C.DNSPrefer @@ -76,6 +77,11 @@ func (b *Base) SupportUDP() bool { return b.udp } +// SupportTFO implements C.ProxyAdapter +func (b *Base) SupportTFO() bool { + return b.tfo +} + // MarshalJSON implements C.ProxyAdapter func (b *Base) MarshalJSON() ([]byte, error) { return json.Marshal(map[string]string{ @@ -130,6 +136,7 @@ type BaseOption struct { Addr string Type C.AdapterType UDP bool + TFO bool Interface string RoutingMark int Prefer C.DNSPrefer @@ -141,6 +148,7 @@ func NewBase(opt BaseOption) *Base { addr: opt.Addr, tp: opt.Type, udp: opt.UDP, + tfo: opt.TFO, iface: opt.Interface, rmark: opt.RoutingMark, prefer: opt.Prefer, diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index 932e8feb..56462399 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -30,12 +30,10 @@ import ( ) const ( - mbpsToBps = 125000 - minSpeedBPS = 16384 + mbpsToBps = 125000 DefaultStreamReceiveWindow = 15728640 // 15 MB/s DefaultConnectionReceiveWindow = 67108864 // 64 MB/s - DefaultMaxIncomingStreams = 1024 DefaultALPN = "hysteria" DefaultProtocol = "udp" @@ -258,6 +256,7 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { addr: addr, tp: C.Hysteria, udp: true, + tfo: option.FastOpen, iface: option.Interface, rmark: option.RoutingMark, prefer: C.NewDNSPrefer(option.IPVersion), diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index 264c991b..38c220de 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -193,6 +193,7 @@ func NewTuic(option TuicOption) (*Tuic, error) { addr: addr, tp: C.Tuic, udp: true, + tfo: option.FastOpen, iface: option.Interface, prefer: C.NewDNSPrefer(option.IPVersion), }, diff --git a/constant/adapters.go b/constant/adapters.go index 53d03fb0..d25023e2 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -86,6 +86,7 @@ type ProxyAdapter interface { Type() AdapterType Addr() string SupportUDP() bool + SupportTFO() bool MarshalJSON() ([]byte, error) // StreamConn wraps a protocol around net.Conn with Metadata. From 2a3c4c1a33357414227d0100896251fb2df59560 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 29 Nov 2022 09:23:28 +0800 Subject: [PATCH 180/250] fix: ensure pool's dial pass to client --- transport/tuic/pool_client.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/transport/tuic/pool_client.go b/transport/tuic/pool_client.go index d4e25157..97e12053 100644 --- a/transport/tuic/pool_client.go +++ b/transport/tuic/pool_client.go @@ -22,6 +22,7 @@ type dialResult struct { type PoolClient struct { *ClientOption + newClientOption *ClientOption dialResultMap map[any]dialResult dialResultMutex *sync.Mutex tcpClients *list.List[*Client] @@ -100,7 +101,7 @@ func (t *PoolClient) newClient(udp bool, opts ...dialer.Option) *Client { clientsMutex.Lock() defer clientsMutex.Unlock() - client := NewClient(t.ClientOption, udp) + client := NewClient(t.newClientOption, udp) client.poolRef = t // make sure pool has a reference client.optionRef = o client.lastVisited = time.Now() @@ -169,6 +170,9 @@ func NewClientPool(clientOption *ClientOption) *PoolClient { udpClients: list.New[*Client](), udpClientsMutex: &sync.Mutex{}, } + newClientOption := *clientOption + newClientOption.DialFn = p.dial + p.newClientOption = &newClientOption runtime.SetFinalizer(p, closeClientPool) return p } From 1d9e320087b82879d65185bf0458b0e55efd4279 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 30 Nov 2022 08:58:53 +0800 Subject: [PATCH 181/250] test: cleanup the test code --- rules/logic/logic_test.go | 114 --------------------------------- rules/logic_test/logic_test.go | 57 +++++++++++++++++ 2 files changed, 57 insertions(+), 114 deletions(-) delete mode 100644 rules/logic/logic_test.go create mode 100644 rules/logic_test/logic_test.go diff --git a/rules/logic/logic_test.go b/rules/logic/logic_test.go deleted file mode 100644 index da061d71..00000000 --- a/rules/logic/logic_test.go +++ /dev/null @@ -1,114 +0,0 @@ -package logic - -import ( - "fmt" - C "github.com/Dreamacro/clash/constant" - RC "github.com/Dreamacro/clash/rules/common" - RP "github.com/Dreamacro/clash/rules/provider" - "github.com/stretchr/testify/assert" - "testing" -) - -func ParseRule(tp, payload, target string, params []string, subRules *map[string][]C.Rule) (parsed C.Rule, parseErr error) { - switch tp { - case "DOMAIN": - parsed = RC.NewDomain(payload, target) - case "DOMAIN-SUFFIX": - parsed = RC.NewDomainSuffix(payload, target) - case "DOMAIN-KEYWORD": - parsed = RC.NewDomainKeyword(payload, target) - case "GEOSITE": - parsed, parseErr = RC.NewGEOSITE(payload, target) - case "GEOIP": - noResolve := RC.HasNoResolve(params) - parsed, parseErr = RC.NewGEOIP(payload, target, noResolve) - case "IP-CIDR", "IP-CIDR6": - noResolve := RC.HasNoResolve(params) - parsed, parseErr = RC.NewIPCIDR(payload, target, RC.WithIPCIDRNoResolve(noResolve)) - case "SRC-IP-CIDR": - parsed, parseErr = RC.NewIPCIDR(payload, target, RC.WithIPCIDRSourceIP(true), RC.WithIPCIDRNoResolve(true)) - case "IP-SUFFIX": - noResolve := RC.HasNoResolve(params) - parsed, parseErr = RC.NewIPSuffix(payload, target, false, noResolve) - case "SRC-IP-SUFFIX": - parsed, parseErr = RC.NewIPSuffix(payload, target, true, true) - case "SRC-PORT": - parsed, parseErr = RC.NewPort(payload, target, C.SrcPort) - case "DST-PORT": - parsed, parseErr = RC.NewPort(payload, target, C.DstPort) - case "IN-PORT": - parsed, parseErr = RC.NewPort(payload, target, C.InPort) - case "PROCESS-NAME": - parsed, parseErr = RC.NewProcess(payload, target, true) - case "PROCESS-PATH": - parsed, parseErr = RC.NewProcess(payload, target, false) - case "NETWORK": - parsed, parseErr = RC.NewNetworkType(payload, target) - case "UID": - parsed, parseErr = RC.NewUid(payload, target) - case "IN-TYPE": - parsed, parseErr = RC.NewInType(payload, target) - case "SUB-RULE": - parsed, parseErr = NewSubRule(payload, target, subRules, ParseRule) - case "AND": - parsed, parseErr = NewAND(payload, target, ParseRule) - case "OR": - parsed, parseErr = NewOR(payload, target, ParseRule) - case "NOT": - parsed, parseErr = NewNOT(payload, target, ParseRule) - case "RULE-SET": - noResolve := RC.HasNoResolve(params) - parsed, parseErr = RP.NewRuleSet(payload, target, noResolve) - case "MATCH": - parsed = RC.NewMatch(target) - parseErr = nil - default: - parseErr = fmt.Errorf("unsupported rule type %s", tp) - } - - return -} - -func TestAND(t *testing.T) { - and, err := NewAND("((DOMAIN,baidu.com),(NETWORK,TCP),(DST-PORT,10001-65535))", "DIRECT", ParseRule) - assert.Equal(t, nil, err) - assert.Equal(t, "DIRECT", and.adapter) - assert.Equal(t, false, and.ShouldResolveIP()) - m, _ := and.Match(&C.Metadata{ - Host: "baidu.com", - NetWork: C.TCP, - DstPort: "20000", - }) - assert.Equal(t, true, m) - - and, err = NewAND("(DOMAIN,baidu.com),(NETWORK,TCP),(DST-PORT,10001-65535))", "DIRECT", ParseRule) - assert.NotEqual(t, nil, err) - - and, err = NewAND("((AND,(DOMAIN,baidu.com),(NETWORK,TCP)),(NETWORK,TCP),(DST-PORT,10001-65535))", "DIRECT", ParseRule) - assert.Equal(t, nil, err) -} - -func TestNOT(t *testing.T) { - not, err := NewNOT("((DST-PORT,6000-6500))", "REJECT", ParseRule) - assert.Equal(t, nil, err) - m, _ := not.Match(&C.Metadata{ - DstPort: "6100", - }) - assert.Equal(t, false, m) - - _, err = NewNOT("((DST-PORT,5600-6666),(DOMAIN,baidu.com))", "DIRECT", ParseRule) - assert.NotEqual(t, nil, err) - - _, err = NewNOT("(())", "DIRECT", ParseRule) - assert.NotEqual(t, nil, err) -} - -func TestOR(t *testing.T) { - or, err := NewOR("((DOMAIN,baidu.com),(NETWORK,TCP),(DST-PORT,10001-65535))", "DIRECT", ParseRule) - assert.Equal(t, nil, err) - m, _ := or.Match(&C.Metadata{ - NetWork: C.TCP, - }) - assert.Equal(t, true, m) - assert.Equal(t, false, or.ShouldResolveIP()) -} diff --git a/rules/logic_test/logic_test.go b/rules/logic_test/logic_test.go new file mode 100644 index 00000000..de5ae569 --- /dev/null +++ b/rules/logic_test/logic_test.go @@ -0,0 +1,57 @@ +package logic_test + +import ( + // https://github.com/golang/go/wiki/CodeReviewComments#import-dot + . "github.com/Dreamacro/clash/rules/logic" + + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/rules" + "github.com/stretchr/testify/assert" + "testing" +) + +var ParseRule = rules.ParseRule + +func TestAND(t *testing.T) { + and, err := NewAND("((DOMAIN,baidu.com),(NETWORK,TCP),(DST-PORT,10001-65535))", "DIRECT", ParseRule) + assert.Equal(t, nil, err) + assert.Equal(t, "DIRECT", and.Adapter()) + assert.Equal(t, false, and.ShouldResolveIP()) + m, _ := and.Match(&C.Metadata{ + Host: "baidu.com", + NetWork: C.TCP, + DstPort: "20000", + }) + assert.Equal(t, true, m) + + and, err = NewAND("(DOMAIN,baidu.com),(NETWORK,TCP),(DST-PORT,10001-65535))", "DIRECT", ParseRule) + assert.NotEqual(t, nil, err) + + and, err = NewAND("((AND,(DOMAIN,baidu.com),(NETWORK,TCP)),(NETWORK,TCP),(DST-PORT,10001-65535))", "DIRECT", ParseRule) + assert.Equal(t, nil, err) +} + +func TestNOT(t *testing.T) { + not, err := NewNOT("((DST-PORT,6000-6500))", "REJECT", ParseRule) + assert.Equal(t, nil, err) + m, _ := not.Match(&C.Metadata{ + DstPort: "6100", + }) + assert.Equal(t, false, m) + + _, err = NewNOT("((DST-PORT,5600-6666),(DOMAIN,baidu.com))", "DIRECT", ParseRule) + assert.NotEqual(t, nil, err) + + _, err = NewNOT("(())", "DIRECT", ParseRule) + assert.NotEqual(t, nil, err) +} + +func TestOR(t *testing.T) { + or, err := NewOR("((DOMAIN,baidu.com),(NETWORK,TCP),(DST-PORT,10001-65535))", "DIRECT", ParseRule) + assert.Equal(t, nil, err) + m, _ := or.Match(&C.Metadata{ + NetWork: C.TCP, + }) + assert.Equal(t, true, m) + assert.Equal(t, false, or.ShouldResolveIP()) +} From 84caee94af864c3135a6ec6ea2ca808933d3a8d1 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 30 Nov 2022 18:50:46 +0800 Subject: [PATCH 182/250] chore: decrease DomainTrie's memory use --- component/trie/domain.go | 10 +++--- component/trie/node.go | 52 ++++++++++++++++++++++++++----- config/config.go | 5 +++ dns/filters.go | 1 + dns/resolver.go | 1 + rules/provider/domain_strategy.go | 1 + 6 files changed, 58 insertions(+), 12 deletions(-) diff --git a/component/trie/domain.go b/component/trie/domain.go index 86c467db..cb4cee94 100644 --- a/component/trie/domain.go +++ b/component/trie/domain.go @@ -73,11 +73,7 @@ func (t *DomainTrie[T]) insert(parts []string, data T) { // reverse storage domain part to save space for i := len(parts) - 1; i >= 0; i-- { part := parts[i] - if !node.hasChild(part) { - node.addChild(part, newNode[T]()) - } - - node = node.getChild(part) + node = node.getOrNewChild(part) } node.setData(data) @@ -123,6 +119,10 @@ func (t *DomainTrie[T]) search(node *Node[T], parts []string) *Node[T] { return node.getChild(dotWildcard) } +func (t *DomainTrie[T]) FinishInsert() { + t.root.finishAdd() +} + // New returns a new, empty Trie. func New[T any]() *DomainTrie[T] { return &DomainTrie[T]{root: newNode[T]()} diff --git a/component/trie/node.go b/component/trie/node.go index 9d45bda8..37570351 100644 --- a/component/trie/node.go +++ b/component/trie/node.go @@ -1,5 +1,7 @@ package trie +import "strings" + // Node is the trie's node type Node[T any] struct { children map[string]*Node[T] @@ -8,6 +10,9 @@ type Node[T any] struct { } func (n *Node[T]) getChild(s string) *Node[T] { + if n.children == nil { + return nil + } return n.children[s] } @@ -16,9 +21,48 @@ func (n *Node[T]) hasChild(s string) bool { } func (n *Node[T]) addChild(s string, child *Node[T]) { + if n.children == nil { + n.children = map[string]*Node[T]{} + } n.children[s] = child } +func (n *Node[T]) getOrNewChild(s string) *Node[T] { + node := n.getChild(s) + if node == nil { + node = newNode[T]() + n.addChild(s, node) + } + return node +} + +func (n *Node[T]) finishAdd() { + if n.children == nil { + return + } + if len(n.children) == 0 { + n.children = nil + } + children := make(map[string]*Node[T], len(n.children)) // avoid map reallocate memory + for key := range n.children { + child := n.children[key] + if child == nil { + continue + } + switch key { // try to save string's memory + case wildcard: + key = wildcard + case dotWildcard: + key = dotWildcard + default: + key = strings.Clone(key) + } + children[key] = child + child.finishAdd() + } + n.children = children +} + func (n *Node[T]) isEmpty() bool { if n == nil || n.inited == false { return true @@ -37,13 +81,7 @@ func (n *Node[T]) Data() T { func newNode[T any]() *Node[T] { return &Node[T]{ - children: map[string]*Node[T]{}, + children: nil, inited: false, - data: getZero[T](), } } - -func getZero[T any]() T { - var result T - return result -} diff --git a/config/config.go b/config/config.go index 9033f162..9b6f60c0 100644 --- a/config/config.go +++ b/config/config.go @@ -972,6 +972,7 @@ func parseHosts(cfg *RawConfig) (*trie.DomainTrie[netip.Addr], error) { _ = tree.Insert(domain, ip) } } + tree.FinishInsert() return tree, nil } @@ -1206,6 +1207,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[netip.Addr], rules []C.R for _, domain := range cfg.FakeIPFilter { _ = host.Insert(domain, struct{}{}) } + host.FinishInsert() } if len(dnsCfg.Fallback) != 0 { @@ -1218,6 +1220,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[netip.Addr], rules []C.R } _ = host.Insert(fb.Addr, struct{}{}) } + host.FinishInsert() } pool, err := fakeip.New(fakeip.Options{ @@ -1393,6 +1396,7 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) { return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err) } } + sniffer.ForceDomain.FinishInsert() sniffer.SkipDomain = trie.New[struct{}]() for _, domain := range snifferRaw.SkipDomain { @@ -1401,6 +1405,7 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) { return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err) } } + sniffer.SkipDomain.FinishInsert() return sniffer, nil } diff --git a/dns/filters.go b/dns/filters.go index 5b0141a2..4fcb8ee4 100644 --- a/dns/filters.go +++ b/dns/filters.go @@ -79,6 +79,7 @@ func NewDomainFilter(domains []string) *domainFilter { for _, domain := range domains { _ = df.tree.Insert(domain, struct{}{}) } + df.tree.FinishInsert() return &df } diff --git a/dns/resolver.go b/dns/resolver.go index 4461a563..501bfb0d 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -435,6 +435,7 @@ func NewResolver(config Config) *Resolver { for domain, nameserver := range config.Policy { _ = r.policy.Insert(domain, NewPolicy(transform([]NameServer{nameserver}, defaultResolver))) } + r.policy.FinishInsert() } fallbackIPFilters := []fallbackIPFilter{} diff --git a/rules/provider/domain_strategy.go b/rules/provider/domain_strategy.go index e8bebf6e..14ff17a4 100644 --- a/rules/provider/domain_strategy.go +++ b/rules/provider/domain_strategy.go @@ -36,6 +36,7 @@ func (d *domainStrategy) OnUpdate(rules []string) { count++ } } + domainTrie.FinishInsert() d.domainRules = domainTrie d.count = count From df8e129fc60e9c1e7c6fc514548d718e26f3aa36 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 30 Nov 2022 19:42:05 +0800 Subject: [PATCH 183/250] chore: optimize DomainTrie for only one child --- component/trie/domain.go | 4 +-- component/trie/node.go | 49 ++++++++++++++++++++++++------- config/config.go | 10 +++---- dns/filters.go | 2 +- dns/resolver.go | 2 +- rules/provider/domain_strategy.go | 2 +- 6 files changed, 49 insertions(+), 20 deletions(-) diff --git a/component/trie/domain.go b/component/trie/domain.go index cb4cee94..d9463c6e 100644 --- a/component/trie/domain.go +++ b/component/trie/domain.go @@ -119,8 +119,8 @@ func (t *DomainTrie[T]) search(node *Node[T], parts []string) *Node[T] { return node.getChild(dotWildcard) } -func (t *DomainTrie[T]) FinishInsert() { - t.root.finishAdd() +func (t *DomainTrie[T]) Optimize() { + t.root.optimize() } // New returns a new, empty Trie. diff --git a/component/trie/node.go b/component/trie/node.go index 37570351..e7baabb6 100644 --- a/component/trie/node.go +++ b/component/trie/node.go @@ -4,13 +4,18 @@ import "strings" // Node is the trie's node type Node[T any] struct { - children map[string]*Node[T] - inited bool - data T + childNode *Node[T] // optimize for only one child + childStr string + children map[string]*Node[T] + inited bool + data T } func (n *Node[T]) getChild(s string) *Node[T] { if n.children == nil { + if n.childNode != nil && n.childStr == s { + return n.childNode + } return nil } return n.children[s] @@ -22,8 +27,19 @@ func (n *Node[T]) hasChild(s string) bool { func (n *Node[T]) addChild(s string, child *Node[T]) { if n.children == nil { + if n.childNode == nil { + n.childStr = s + n.childNode = child + return + } n.children = map[string]*Node[T]{} + if n.childNode != nil { + n.children[n.childStr] = n.childNode + } + n.childStr = "" + n.childNode = nil } + n.children[s] = child } @@ -36,12 +52,28 @@ func (n *Node[T]) getOrNewChild(s string) *Node[T] { return node } -func (n *Node[T]) finishAdd() { +func (n *Node[T]) optimize() { + if len(n.childStr) > 0 { + n.childStr = strings.Clone(n.childStr) + } + if n.childNode != nil { + n.childNode.optimize() + } if n.children == nil { return } - if len(n.children) == 0 { + switch len(n.children) { + case 0: n.children = nil + return + case 1: + for key := range n.children { + n.childStr = key + n.childNode = n.children[key] + } + n.children = nil + n.optimize() + return } children := make(map[string]*Node[T], len(n.children)) // avoid map reallocate memory for key := range n.children { @@ -58,7 +90,7 @@ func (n *Node[T]) finishAdd() { key = strings.Clone(key) } children[key] = child - child.finishAdd() + child.optimize() } n.children = children } @@ -80,8 +112,5 @@ func (n *Node[T]) Data() T { } func newNode[T any]() *Node[T] { - return &Node[T]{ - children: nil, - inited: false, - } + return &Node[T]{} } diff --git a/config/config.go b/config/config.go index 9b6f60c0..bb8c65f4 100644 --- a/config/config.go +++ b/config/config.go @@ -972,7 +972,7 @@ func parseHosts(cfg *RawConfig) (*trie.DomainTrie[netip.Addr], error) { _ = tree.Insert(domain, ip) } } - tree.FinishInsert() + tree.Optimize() return tree, nil } @@ -1207,7 +1207,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[netip.Addr], rules []C.R for _, domain := range cfg.FakeIPFilter { _ = host.Insert(domain, struct{}{}) } - host.FinishInsert() + host.Optimize() } if len(dnsCfg.Fallback) != 0 { @@ -1220,7 +1220,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie[netip.Addr], rules []C.R } _ = host.Insert(fb.Addr, struct{}{}) } - host.FinishInsert() + host.Optimize() } pool, err := fakeip.New(fakeip.Options{ @@ -1396,7 +1396,7 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) { return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err) } } - sniffer.ForceDomain.FinishInsert() + sniffer.ForceDomain.Optimize() sniffer.SkipDomain = trie.New[struct{}]() for _, domain := range snifferRaw.SkipDomain { @@ -1405,7 +1405,7 @@ func parseSniffer(snifferRaw RawSniffer) (*Sniffer, error) { return nil, fmt.Errorf("error domian[%s] in force-domain, error:%v", domain, err) } } - sniffer.SkipDomain.FinishInsert() + sniffer.SkipDomain.Optimize() return sniffer, nil } diff --git a/dns/filters.go b/dns/filters.go index 4fcb8ee4..0dbfa317 100644 --- a/dns/filters.go +++ b/dns/filters.go @@ -79,7 +79,7 @@ func NewDomainFilter(domains []string) *domainFilter { for _, domain := range domains { _ = df.tree.Insert(domain, struct{}{}) } - df.tree.FinishInsert() + df.tree.Optimize() return &df } diff --git a/dns/resolver.go b/dns/resolver.go index 501bfb0d..d99a465d 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -435,7 +435,7 @@ func NewResolver(config Config) *Resolver { for domain, nameserver := range config.Policy { _ = r.policy.Insert(domain, NewPolicy(transform([]NameServer{nameserver}, defaultResolver))) } - r.policy.FinishInsert() + r.policy.Optimize() } fallbackIPFilters := []fallbackIPFilter{} diff --git a/rules/provider/domain_strategy.go b/rules/provider/domain_strategy.go index 14ff17a4..61fe93a6 100644 --- a/rules/provider/domain_strategy.go +++ b/rules/provider/domain_strategy.go @@ -36,7 +36,7 @@ func (d *domainStrategy) OnUpdate(rules []string) { count++ } } - domainTrie.FinishInsert() + domainTrie.Optimize() d.domainRules = domainTrie d.count = count From bc5ab3120f223608c102fc931c5d16a330a66e07 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 30 Nov 2022 20:38:03 +0800 Subject: [PATCH 184/250] fix: use same strClone function --- component/trie/node.go | 61 ++++++++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/component/trie/node.go b/component/trie/node.go index e7baabb6..e19b40ac 100644 --- a/component/trie/node.go +++ b/component/trie/node.go @@ -4,21 +4,21 @@ import "strings" // Node is the trie's node type Node[T any] struct { + childMap map[string]*Node[T] childNode *Node[T] // optimize for only one child childStr string - children map[string]*Node[T] inited bool data T } func (n *Node[T]) getChild(s string) *Node[T] { - if n.children == nil { + if n.childMap == nil { if n.childNode != nil && n.childStr == s { return n.childNode } return nil } - return n.children[s] + return n.childMap[s] } func (n *Node[T]) hasChild(s string) bool { @@ -26,21 +26,21 @@ func (n *Node[T]) hasChild(s string) bool { } func (n *Node[T]) addChild(s string, child *Node[T]) { - if n.children == nil { + if n.childMap == nil { if n.childNode == nil { n.childStr = s n.childNode = child return } - n.children = map[string]*Node[T]{} + n.childMap = map[string]*Node[T]{} if n.childNode != nil { - n.children[n.childStr] = n.childNode + n.childMap[n.childStr] = n.childNode } n.childStr = "" n.childNode = nil } - n.children[s] = child + n.childMap[s] = child } func (n *Node[T]) getOrNewChild(s string) *Node[T] { @@ -54,45 +54,54 @@ func (n *Node[T]) getOrNewChild(s string) *Node[T] { func (n *Node[T]) optimize() { if len(n.childStr) > 0 { - n.childStr = strings.Clone(n.childStr) + n.childStr = strClone(n.childStr) } if n.childNode != nil { n.childNode.optimize() } - if n.children == nil { + if n.childMap == nil { return } - switch len(n.children) { + switch len(n.childMap) { case 0: - n.children = nil + n.childMap = nil return case 1: - for key := range n.children { + for key := range n.childMap { n.childStr = key - n.childNode = n.children[key] + n.childNode = n.childMap[key] } - n.children = nil + n.childMap = nil n.optimize() return } - children := make(map[string]*Node[T], len(n.children)) // avoid map reallocate memory - for key := range n.children { - child := n.children[key] + children := make(map[string]*Node[T], len(n.childMap)) // avoid map reallocate memory + for key := range n.childMap { + child := n.childMap[key] if child == nil { continue } - switch key { // try to save string's memory - case wildcard: - key = wildcard - case dotWildcard: - key = dotWildcard - default: - key = strings.Clone(key) - } + key = strClone(key) children[key] = child child.optimize() } - n.children = children + n.childMap = children +} + +func strClone(key string) string { + switch key { // try to save string's memory + case wildcard: + key = wildcard + case dotWildcard: + key = dotWildcard + case complexWildcard: + key = complexWildcard + case domainStep: + key = domainStep + default: + key = strings.Clone(key) + } + return key } func (n *Node[T]) isEmpty() bool { From 0aefa3be85e84e8e004ac70c5b685faab3dc7dcf Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 2 Dec 2022 16:56:17 +0800 Subject: [PATCH 185/250] fix: remove cyclic dependent to make tuic's Finalizer work --- adapter/outbound/tuic.go | 7 +-- adapter/outbound/wireguard.go | 91 +-------------------------- common/net/refconn.go | 100 ++++++++++++++++++++++++++++++ transport/tuic/client.go | 112 +++++++++++++++++++++------------- transport/tuic/conn.go | 4 -- transport/tuic/pool_client.go | 52 ++++++++++------ transport/tuic/server.go | 2 - 7 files changed, 209 insertions(+), 159 deletions(-) create mode 100644 common/net/refconn.go diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index 38c220de..5afaa895 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -56,7 +56,7 @@ type TuicOption struct { // DialContext implements C.ProxyAdapter func (t *Tuic) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { opts = t.Base.DialOptions(opts...) - conn, err := t.client.DialContext(ctx, metadata, opts...) + conn, err := t.client.DialContext(ctx, metadata, t.dial, opts...) if err != nil { return nil, err } @@ -66,7 +66,7 @@ func (t *Tuic) DialContext(ctx context.Context, metadata *C.Metadata, opts ...di // ListenPacketContext implements C.ProxyAdapter func (t *Tuic) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { opts = t.Base.DialOptions(opts...) - pc, err := t.client.ListenPacketContext(ctx, metadata, opts...) + pc, err := t.client.ListenPacketContext(ctx, metadata, t.dial, opts...) if err != nil { return nil, err } @@ -205,7 +205,6 @@ func NewTuic(option TuicOption) (*Tuic, error) { clientMaxOpenStreams = 1 } clientOption := &tuic.ClientOption{ - DialFn: t.dial, TlsConfig: tlsConfig, QuicConfig: quicConfig, Host: host, @@ -219,7 +218,7 @@ func NewTuic(option TuicOption) (*Tuic, error) { MaxOpenStreams: clientMaxOpenStreams, } - t.client = tuic.NewClientPool(clientOption) + t.client = tuic.NewPoolClient(clientOption) return t, nil } diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index d08c0325..eb35ba03 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -12,8 +12,8 @@ import ( "strconv" "strings" "sync" - "time" + CN "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/resolver" C "github.com/Dreamacro/clash/constant" @@ -220,7 +220,7 @@ func (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata, opts if conn == nil { return nil, E.New("conn is nil") } - return NewConn(&wgConn{conn, w}, w), nil + return NewConn(CN.NewRefConn(conn, w), w), nil } func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { @@ -249,90 +249,5 @@ func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadat if pc == nil { return nil, E.New("packetConn is nil") } - return newPacketConn(&wgPacketConn{pc, w}, w), nil -} - -type wgConn struct { - conn net.Conn - wg *WireGuard -} - -func (c *wgConn) Read(b []byte) (n int, err error) { - defer runtime.KeepAlive(c.wg) - return c.conn.Read(b) -} - -func (c *wgConn) Write(b []byte) (n int, err error) { - defer runtime.KeepAlive(c.wg) - return c.conn.Write(b) -} - -func (c *wgConn) Close() error { - defer runtime.KeepAlive(c.wg) - return c.conn.Close() -} - -func (c *wgConn) LocalAddr() net.Addr { - defer runtime.KeepAlive(c.wg) - return c.conn.LocalAddr() -} - -func (c *wgConn) RemoteAddr() net.Addr { - defer runtime.KeepAlive(c.wg) - return c.conn.RemoteAddr() -} - -func (c *wgConn) SetDeadline(t time.Time) error { - defer runtime.KeepAlive(c.wg) - return c.conn.SetDeadline(t) -} - -func (c *wgConn) SetReadDeadline(t time.Time) error { - defer runtime.KeepAlive(c.wg) - return c.conn.SetReadDeadline(t) -} - -func (c *wgConn) SetWriteDeadline(t time.Time) error { - defer runtime.KeepAlive(c.wg) - return c.conn.SetWriteDeadline(t) -} - -type wgPacketConn struct { - pc net.PacketConn - wg *WireGuard -} - -func (pc *wgPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { - defer runtime.KeepAlive(pc.wg) - return pc.pc.ReadFrom(p) -} - -func (pc *wgPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { - defer runtime.KeepAlive(pc.wg) - return pc.pc.WriteTo(p, addr) -} - -func (pc *wgPacketConn) Close() error { - defer runtime.KeepAlive(pc.wg) - return pc.pc.Close() -} - -func (pc *wgPacketConn) LocalAddr() net.Addr { - defer runtime.KeepAlive(pc.wg) - return pc.pc.LocalAddr() -} - -func (pc *wgPacketConn) SetDeadline(t time.Time) error { - defer runtime.KeepAlive(pc.wg) - return pc.pc.SetDeadline(t) -} - -func (pc *wgPacketConn) SetReadDeadline(t time.Time) error { - defer runtime.KeepAlive(pc.wg) - return pc.pc.SetReadDeadline(t) -} - -func (pc *wgPacketConn) SetWriteDeadline(t time.Time) error { - defer runtime.KeepAlive(pc.wg) - return pc.pc.SetWriteDeadline(t) + return newPacketConn(CN.NewRefPacketConn(pc, w), w), nil } diff --git a/common/net/refconn.go b/common/net/refconn.go new file mode 100644 index 00000000..6d28a2bf --- /dev/null +++ b/common/net/refconn.go @@ -0,0 +1,100 @@ +package net + +import ( + "net" + "runtime" + "time" +) + +type refConn struct { + conn net.Conn + ref any +} + +func (c *refConn) Read(b []byte) (n int, err error) { + defer runtime.KeepAlive(c.ref) + return c.conn.Read(b) +} + +func (c *refConn) Write(b []byte) (n int, err error) { + defer runtime.KeepAlive(c.ref) + return c.conn.Write(b) +} + +func (c *refConn) Close() error { + defer runtime.KeepAlive(c.ref) + return c.conn.Close() +} + +func (c *refConn) LocalAddr() net.Addr { + defer runtime.KeepAlive(c.ref) + return c.conn.LocalAddr() +} + +func (c *refConn) RemoteAddr() net.Addr { + defer runtime.KeepAlive(c.ref) + return c.conn.RemoteAddr() +} + +func (c *refConn) SetDeadline(t time.Time) error { + defer runtime.KeepAlive(c.ref) + return c.conn.SetDeadline(t) +} + +func (c *refConn) SetReadDeadline(t time.Time) error { + defer runtime.KeepAlive(c.ref) + return c.conn.SetReadDeadline(t) +} + +func (c *refConn) SetWriteDeadline(t time.Time) error { + defer runtime.KeepAlive(c.ref) + return c.conn.SetWriteDeadline(t) +} + +func NewRefConn(conn net.Conn, ref any) net.Conn { + return &refConn{conn: conn, ref: ref} +} + +type refPacketConn struct { + pc net.PacketConn + ref any +} + +func (pc *refPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) { + defer runtime.KeepAlive(pc.ref) + return pc.pc.ReadFrom(p) +} + +func (pc *refPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { + defer runtime.KeepAlive(pc.ref) + return pc.pc.WriteTo(p, addr) +} + +func (pc *refPacketConn) Close() error { + defer runtime.KeepAlive(pc.ref) + return pc.pc.Close() +} + +func (pc *refPacketConn) LocalAddr() net.Addr { + defer runtime.KeepAlive(pc.ref) + return pc.pc.LocalAddr() +} + +func (pc *refPacketConn) SetDeadline(t time.Time) error { + defer runtime.KeepAlive(pc.ref) + return pc.pc.SetDeadline(t) +} + +func (pc *refPacketConn) SetReadDeadline(t time.Time) error { + defer runtime.KeepAlive(pc.ref) + return pc.pc.SetReadDeadline(t) +} + +func (pc *refPacketConn) SetWriteDeadline(t time.Time) error { + defer runtime.KeepAlive(pc.ref) + return pc.pc.SetWriteDeadline(t) +} + +func NewRefPacketConn(pc net.PacketConn, ref any) net.PacketConn { + return &refPacketConn{pc: pc, ref: ref} +} diff --git a/transport/tuic/client.go b/transport/tuic/client.go index a1dfcc30..dcb4e3aa 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -19,6 +19,7 @@ import ( "github.com/Dreamacro/clash/common/pool" "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/log" ) var ( @@ -26,9 +27,9 @@ var ( TooManyOpenStreams = errors.New("tuic: too many open streams") ) -type ClientOption struct { - DialFn func(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) +type DialFunc func(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) +type ClientOption struct { TlsConfig *tls.Config QuicConfig *quic.Config Host string @@ -42,7 +43,7 @@ type ClientOption struct { MaxOpenStreams int64 } -type Client struct { +type clientImpl struct { *ClientOption udp bool @@ -55,18 +56,17 @@ type Client struct { udpInputMap sync.Map // only ready for PoolClient - poolRef *PoolClient optionRef any lastVisited time.Time } -func (t *Client) getQuicConn(ctx context.Context) (quic.Connection, error) { +func (t *clientImpl) getQuicConn(ctx context.Context, dialFn DialFunc, opts ...dialer.Option) (quic.Connection, error) { t.connMutex.Lock() defer t.connMutex.Unlock() if t.quicConn != nil { return t.quicConn, nil } - pc, addr, err := t.DialFn(ctx) + pc, addr, err := dialFn(ctx, opts...) if err != nil { return nil, err } @@ -97,7 +97,7 @@ func (t *Client) getQuicConn(ctx context.Context) (quic.Connection, error) { return quicConn, nil } -func (t *Client) sendAuthentication(quicConn quic.Connection) (err error) { +func (t *clientImpl) sendAuthentication(quicConn quic.Connection) (err error) { defer func() { t.deferQuicConn(quicConn, err) }() @@ -122,7 +122,7 @@ func (t *Client) sendAuthentication(quicConn quic.Connection) (err error) { return nil } -func (t *Client) parseUDP(quicConn quic.Connection) (err error) { +func (t *clientImpl) parseUDP(quicConn quic.Connection) (err error) { defer func() { t.deferQuicConn(quicConn, err) }() @@ -199,45 +199,50 @@ func (t *Client) parseUDP(quicConn quic.Connection) (err error) { } } -func (t *Client) deferQuicConn(quicConn quic.Connection, err error) { +func (t *clientImpl) deferQuicConn(quicConn quic.Connection, err error) { var netError net.Error if err != nil && errors.As(err, &netError) { - t.connMutex.Lock() - defer t.connMutex.Unlock() - if t.quicConn == quicConn { - t.forceClose(err, true) + t.forceClose(quicConn, err) + } +} + +func (t *clientImpl) forceClose(quicConn quic.Connection, err error) { + t.connMutex.Lock() + defer t.connMutex.Unlock() + if quicConn == nil { + quicConn = t.quicConn + } + if quicConn != nil { + if quicConn == t.quicConn { + t.quicConn = nil } } -} - -func (t *Client) forceClose(err error, locked bool) { - if !locked { - t.connMutex.Lock() - defer t.connMutex.Unlock() + errStr := "" + if err != nil { + errStr = err.Error() } - quicConn := t.quicConn if quicConn != nil { - _ = quicConn.CloseWithError(ProtocolError, err.Error()) - t.udpInputMap.Range(func(key, value any) bool { - if conn, ok := value.(net.Conn); ok { - _ = conn.Close() - } - t.udpInputMap.Delete(key) - return true - }) - t.quicConn = nil + _ = quicConn.CloseWithError(ProtocolError, errStr) } + udpInputMap := &t.udpInputMap + udpInputMap.Range(func(key, value any) bool { + if conn, ok := value.(net.Conn); ok { + _ = conn.Close() + } + udpInputMap.Delete(key) + return true + }) } -func (t *Client) Close() { +func (t *clientImpl) Close() { t.closed.Store(true) if t.openStreams.Load() == 0 { - t.forceClose(ClientClosed, false) + t.forceClose(nil, ClientClosed) } } -func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata) (net.Conn, error) { - quicConn, err := t.getQuicConn(ctx) +func (t *clientImpl) DialContext(ctx context.Context, metadata *C.Metadata, dialFn DialFunc, opts ...dialer.Option) (net.Conn, error) { + quicConn, err := t.getQuicConn(ctx, dialFn, opts...) if err != nil { return nil, err } @@ -264,12 +269,11 @@ func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata) (net.Con Stream: quicStream, lAddr: quicConn.LocalAddr(), rAddr: quicConn.RemoteAddr(), - ref: t, closeDeferFn: func() { time.AfterFunc(C.DefaultTCPTimeout, func() { openStreams := t.openStreams.Add(-1) if openStreams == 0 && t.closed.Load() { - t.forceClose(ClientClosed, false) + t.forceClose(quicConn, ClientClosed) } }) }, @@ -335,8 +339,8 @@ func (conn *earlyConn) Read(b []byte) (n int, err error) { return conn.BufferedConn.Read(b) } -func (t *Client) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (net.PacketConn, error) { - quicConn, err := t.getQuicConn(ctx) +func (t *clientImpl) ListenPacketContext(ctx context.Context, metadata *C.Metadata, dialFn DialFunc, opts ...dialer.Option) (net.PacketConn, error) { + quicConn, err := t.getQuicConn(ctx, dialFn, opts...) if err != nil { return nil, err } @@ -362,14 +366,13 @@ func (t *Client) ListenPacketContext(ctx context.Context, metadata *C.Metadata) inputConn: N.NewBufferedConn(pipe2), udpRelayMode: t.UdpRelayMode, maxUdpRelayPacketSize: t.MaxUdpRelayPacketSize, - ref: t, deferQuicConnFn: t.deferQuicConn, closeDeferFn: func() { t.udpInputMap.Delete(connId) time.AfterFunc(C.DefaultUDPTimeout, func() { openStreams := t.openStreams.Add(-1) if openStreams == 0 && t.closed.Load() { - t.forceClose(ClientClosed, false) + t.forceClose(quicConn, ClientClosed) } }) }, @@ -377,15 +380,42 @@ func (t *Client) ListenPacketContext(ctx context.Context, metadata *C.Metadata) return pc, nil } +type Client struct { + *clientImpl // use an independent pointer to let Finalizer can work no matter somewhere handle an influence in clientImpl inner +} + +func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata, dialFn DialFunc, opts ...dialer.Option) (net.Conn, error) { + conn, err := t.clientImpl.DialContext(ctx, metadata, dialFn, opts...) + if err != nil { + return nil, err + } + return N.NewRefConn(conn, t), err +} + +func (t *Client) ListenPacketContext(ctx context.Context, metadata *C.Metadata, dialFn DialFunc, opts ...dialer.Option) (net.PacketConn, error) { + pc, err := t.clientImpl.ListenPacketContext(ctx, metadata, dialFn, opts...) + if err != nil { + return nil, err + } + return N.NewRefPacketConn(pc, t), nil +} + +func (t *Client) forceClose() { + t.clientImpl.forceClose(nil, ClientClosed) +} + func NewClient(clientOption *ClientOption, udp bool) *Client { - c := &Client{ + ci := &clientImpl{ ClientOption: clientOption, udp: udp, } + c := &Client{ci} runtime.SetFinalizer(c, closeClient) + log.Debugln("New Tuic Client at %p", c) return c } func closeClient(client *Client) { - client.forceClose(ClientClosed, false) + log.Debugln("Close Tuic Client at %p", client) + client.forceClose() } diff --git a/transport/tuic/conn.go b/transport/tuic/conn.go index 3e759f25..81e8c40e 100644 --- a/transport/tuic/conn.go +++ b/transport/tuic/conn.go @@ -58,8 +58,6 @@ type quicStreamConn struct { lAddr net.Addr rAddr net.Addr - ref any - closeDeferFn func() closeOnce sync.Once @@ -117,8 +115,6 @@ type quicStreamPacketConn struct { udpRelayMode string maxUdpRelayPacketSize int - ref any - deferQuicConnFn func(quicConn quic.Connection, err error) closeDeferFn func() writeClosed *atomic.Bool diff --git a/transport/tuic/pool_client.go b/transport/tuic/pool_client.go index 97e12053..9753da0d 100644 --- a/transport/tuic/pool_client.go +++ b/transport/tuic/pool_client.go @@ -9,8 +9,10 @@ import ( "time" "github.com/Dreamacro/clash/common/generics/list" + N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/log" ) type dialResult struct { @@ -31,29 +33,35 @@ type PoolClient struct { udpClientsMutex *sync.Mutex } -func (t *PoolClient) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (net.Conn, error) { - conn, err := t.getClient(false, opts...).DialContext(ctx, metadata) +func (t *PoolClient) DialContext(ctx context.Context, metadata *C.Metadata, dialFn DialFunc, opts ...dialer.Option) (net.Conn, error) { + newDialFn := func(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) { + return t.dial(ctx, dialFn, opts...) + } + conn, err := t.getClient(false, opts...).DialContext(ctx, metadata, newDialFn, opts...) if errors.Is(err, TooManyOpenStreams) { - conn, err = t.newClient(false, opts...).DialContext(ctx, metadata) + conn, err = t.newClient(false, opts...).DialContext(ctx, metadata, newDialFn, opts...) } if err != nil { return nil, err } - return conn, err + return N.NewRefConn(conn, t), err } -func (t *PoolClient) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (net.PacketConn, error) { - pc, err := t.getClient(true, opts...).ListenPacketContext(ctx, metadata) +func (t *PoolClient) ListenPacketContext(ctx context.Context, metadata *C.Metadata, dialFn DialFunc, opts ...dialer.Option) (net.PacketConn, error) { + newDialFn := func(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) { + return t.dial(ctx, dialFn, opts...) + } + pc, err := t.getClient(true, opts...).ListenPacketContext(ctx, metadata, newDialFn, opts...) if errors.Is(err, TooManyOpenStreams) { - pc, err = t.newClient(false, opts...).ListenPacketContext(ctx, metadata) + pc, err = t.newClient(false, opts...).ListenPacketContext(ctx, metadata, newDialFn, opts...) } if err != nil { return nil, err } - return pc, nil + return N.NewRefPacketConn(pc, t), nil } -func (t *PoolClient) dial(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) { +func (t *PoolClient) dial(ctx context.Context, dialFn DialFunc, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) { var o any = *dialer.ApplyOptions(opts...) t.dialResultMutex.Lock() @@ -63,7 +71,7 @@ func (t *PoolClient) dial(ctx context.Context, opts ...dialer.Option) (pc net.Pa return dr.pc, dr.addr, dr.err } - pc, addr, err = t.DialFn(ctx, opts...) + pc, addr, err = dialFn(ctx, opts...) if err != nil { return nil, nil, err } @@ -102,7 +110,6 @@ func (t *PoolClient) newClient(udp bool, opts ...dialer.Option) *Client { defer clientsMutex.Unlock() client := NewClient(t.newClientOption, udp) - client.poolRef = t // make sure pool has a reference client.optionRef = o client.lastVisited = time.Now() @@ -141,16 +148,20 @@ func (t *PoolClient) getClient(udp bool, opts ...dialer.Option) *Client { } } } - if client.openStreams.Load() == 0 && time.Now().Sub(client.lastVisited) > 30*time.Minute { - client.Close() - next := it.Next() - clients.Remove(it) - it = next - continue - } it = it.Next() } }() + for it := clients.Front(); it != nil; { + client := it.Value + if client != bestClient && client.openStreams.Load() == 0 && time.Now().Sub(client.lastVisited) > 30*time.Minute { + client.Close() + next := it.Next() + clients.Remove(it) + it = next + continue + } + it = it.Next() + } if bestClient == nil { return t.newClient(udp, opts...) @@ -160,7 +171,7 @@ func (t *PoolClient) getClient(udp bool, opts ...dialer.Option) *Client { } } -func NewClientPool(clientOption *ClientOption) *PoolClient { +func NewPoolClient(clientOption *ClientOption) *PoolClient { p := &PoolClient{ ClientOption: clientOption, dialResultMap: make(map[any]dialResult), @@ -171,12 +182,13 @@ func NewClientPool(clientOption *ClientOption) *PoolClient { udpClientsMutex: &sync.Mutex{}, } newClientOption := *clientOption - newClientOption.DialFn = p.dial p.newClientOption = &newClientOption runtime.SetFinalizer(p, closeClientPool) + log.Debugln("New Tuic PoolClient at %p", p) return p } func closeClientPool(client *PoolClient) { + log.Debugln("Close Tuic PoolClient at %p", client) client.forceClose() } diff --git a/transport/tuic/server.go b/transport/tuic/server.go index c1213d68..3f459fd6 100644 --- a/transport/tuic/server.go +++ b/transport/tuic/server.go @@ -143,7 +143,6 @@ func (s *serverHandler) parsePacket(packet Packet, udpRelayMode string) (err err inputConn: nil, udpRelayMode: udpRelayMode, maxUdpRelayPacketSize: s.MaxUdpRelayPacketSize, - ref: s, deferQuicConnFn: nil, closeDeferFn: nil, writeClosed: writeClosed, @@ -173,7 +172,6 @@ func (s *serverHandler) handleStream() (err error) { Stream: quicStream, lAddr: s.quicConn.LocalAddr(), rAddr: s.quicConn.RemoteAddr(), - ref: s, } conn := N.NewBufferedConn(stream) connect, err := ReadConnect(conn) From 6c79d9e63beb3bcb5a90bc590d73e173039dc074 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Fri, 2 Dec 2022 20:21:15 +0800 Subject: [PATCH 186/250] chore: fixed v2fly v4 version for test --- test/clash_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/clash_test.go b/test/clash_test.go index e6112e21..3fdca5d0 100644 --- a/test/clash_test.go +++ b/test/clash_test.go @@ -30,7 +30,7 @@ import ( const ( ImageShadowsocks = "mritd/shadowsocks:latest" ImageShadowsocksRust = "ghcr.io/shadowsocks/ssserver-rust:latest" - ImageVmess = "v2fly/v2fly-core:latest" + ImageVmess = "v2fly/v2fly-core:v4.45.2" ImageVmessLatest = "sagernet/v2fly-core:latest" ImageVless = "teddysun/xray:latest" ImageTrojan = "trojangfw/trojan:latest" From 654cdf3d5bf225302817ddb8a5a49c0799c7482e Mon Sep 17 00:00:00 2001 From: Skyxim Date: Fri, 2 Dec 2022 20:21:31 +0800 Subject: [PATCH 187/250] chore: adjust fakeip test --- test/dns_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/dns_test.go b/test/dns_test.go index 1e4c010b..8e30ba98 100644 --- a/test/dns_test.go +++ b/test/dns_test.go @@ -78,9 +78,9 @@ dns: } list := []domainPair{ - {"foo.org", "198.18.0.3"}, - {"bar.org", "198.18.0.4"}, - {"foo.org", "198.18.0.3"}, + {"foo.org", "198.18.0.4"}, + {"bar.org", "198.18.0.5"}, + {"foo.org", "198.18.0.4"}, {"foo.clash.dev", "1.1.1.1"}, } From cf5709aab10e6523be3cf76da2e114773eafc610 Mon Sep 17 00:00:00 2001 From: metacubex Date: Sat, 3 Dec 2022 00:10:50 +0800 Subject: [PATCH 188/250] fix: a temporary solution for error reporting when enabling tun for devices that do not have an ipv6 environment. --- config/config.go | 4 ++++ config/utils.go | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/config/config.go b/config/config.go index bb8c65f4..f5efc936 100644 --- a/config/config.go +++ b/config/config.go @@ -1289,6 +1289,10 @@ func parseTun(rawTun RawTun, general *General) error { } tunAddressPrefix = netip.PrefixFrom(tunAddressPrefix.Addr(), 30) + if !general.IPv6 || !verifyIP6() { + rawTun.Inet6Address = nil + } + general.Tun = Tun{ Enable: rawTun.Enable, Device: rawTun.Device, diff --git a/config/utils.go b/config/utils.go index 1d49552f..2c470618 100644 --- a/config/utils.go +++ b/config/utils.go @@ -2,6 +2,7 @@ package config import ( "fmt" + "net" "strings" "github.com/Dreamacro/clash/adapter/outboundgroup" @@ -146,3 +147,25 @@ func proxyGroupsDagSort(groupsConfig []map[string]any) error { } return fmt.Errorf("loop is detected in ProxyGroup, please check following ProxyGroups: %v", loopElements) } + +func verifyIP6() bool { + addrs, err := net.InterfaceAddrs() + if err != nil { + return false + } + for _, addr := range addrs { + ipNet, isIpNet := addr.(*net.IPNet) + if isIpNet && !ipNet.IP.IsLoopback() { + if ipNet.IP.To16() != nil { + s := ipNet.IP.String() + for i := 0; i < len(s); i++ { + switch s[i] { + case ':': + return true + } + } + } + } + } + return false +} From 2fe271f19f580ddc677dfb0449f26b47cdecf09b Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sat, 3 Dec 2022 12:25:10 +0800 Subject: [PATCH 189/250] feat: add tls port for RESTful api and external controller --- config/config.go | 9 +++++++++ hub/hub.go | 9 ++++++++- hub/route/server.go | 40 ++++++++++++++++++++++++++++++++++++---- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/config/config.go b/config/config.go index f5efc936..39b75e54 100644 --- a/config/config.go +++ b/config/config.go @@ -80,6 +80,9 @@ type Controller struct { ExternalController string `json:"-"` ExternalUI string `json:"-"` Secret string `json:"-"` + TLSPort int `json:"-"` + Cert string `json:"-"` + PrivateKey string `json:"-"` } // DNS config @@ -230,6 +233,9 @@ type Sniffer struct { // Experimental config type Experimental struct { Fingerprints []string `yaml:"fingerprints"` + TLSPort int `yaml:"tls-port,omitempty"` + Cert string `yaml:"cert,omitempty"` + PrivateKey string `yaml:"private-key,omitempty"` } // Config is clash config manager @@ -669,6 +675,9 @@ func parseGeneral(cfg *RawConfig) (*General, error) { ExternalController: cfg.ExternalController, ExternalUI: cfg.ExternalUI, Secret: cfg.Secret, + TLSPort: cfg.Experimental.TLSPort, + Cert: cfg.Experimental.Cert, + PrivateKey: cfg.Experimental.PrivateKey, }, UnifiedDelay: cfg.UnifiedDelay, Mode: cfg.Mode, diff --git a/hub/hub.go b/hub/hub.go index 471fdb5e..216a0c68 100644 --- a/hub/hub.go +++ b/hub/hub.go @@ -1,6 +1,8 @@ package hub import ( + "errors" + "github.com/Dreamacro/clash/config" "github.com/Dreamacro/clash/hub/executor" "github.com/Dreamacro/clash/hub/route" @@ -42,7 +44,12 @@ func Parse(options ...Option) error { } if cfg.General.ExternalController != "" { - go route.Start(cfg.General.ExternalController, cfg.General.Secret) + if cfg.General.TLSPort != 0 && (len(cfg.General.PrivateKey) == 0 || len(cfg.General.Cert) == 0) { + return errors.New("Must be provided certificates and keys, for tls controller") + } + + go route.Start(cfg.General.ExternalController, cfg.General.Secret, cfg.General.TLSPort, + cfg.General.Cert, cfg.General.PrivateKey) } executor.ApplyConfig(cfg, true) diff --git a/hub/route/server.go b/hub/route/server.go index 2d533496..2b449c2d 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -2,8 +2,11 @@ package route import ( "bytes" + "crypto/tls" "encoding/json" + "net" "net/http" + "strconv" "strings" "time" @@ -40,7 +43,7 @@ func SetUIPath(path string) { uiPath = C.Path.Resolve(path) } -func Start(addr string, secret string) { +func Start(addr string, secret string, tlsPort int, cert string, privateKey string) { if serverAddr != "" { return } @@ -49,18 +52,16 @@ func Start(addr string, secret string) { serverSecret = secret r := chi.NewRouter() - corsM := cors.New(cors.Options{ AllowedOrigins: []string{"*"}, AllowedMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE"}, AllowedHeaders: []string{"Content-Type", "Authorization"}, MaxAge: 300, }) - + r.Use() r.Use(corsM.Handler) r.Group(func(r chi.Router) { r.Use(authentication) - r.Get("/", hello) r.Get("/logs", getLogs) r.Get("/traffic", traffic) @@ -84,6 +85,34 @@ func Start(addr string, secret string) { }) }) } + if tlsPort >0 { + go func() { + if host, _, err := net.SplitHostPort(addr); err != nil { + log.Errorln("External controller tls serve error,%s", err) + } else { + l, err := inbound.Listen("tcp", net.JoinHostPort(host, strconv.Itoa(tlsPort))) + if err != nil { + log.Errorln("External controller tls listen error: %s", err) + return + } + serverAddr = l.Addr().String() + log.Infoln("RESTful API tls listening at: %s", serverAddr) + certificate, err := tls.X509KeyPair([]byte(cert), []byte(privateKey)) + if err != nil { + log.Errorln("External controller tls sevre error,%s", err) + } + tlsServe := &http.Server{ + Handler: r, + TLSConfig: &tls.Config{ + Certificates: []tls.Certificate{certificate}, + }, + } + if err = tlsServe.ServeTLS(l, "", ""); err != nil { + log.Errorln("External controller tls serve error: %s", err) + } + } + }() + } l, err := inbound.Listen("tcp", addr) if err != nil { @@ -92,9 +121,12 @@ func Start(addr string, secret string) { } serverAddr = l.Addr().String() log.Infoln("RESTful API listening at: %s", serverAddr) + if err = http.Serve(l, r); err != nil { log.Errorln("External controller serve error: %s", err) } + + } func authentication(next http.Handler) http.Handler { From ba884c29bddf19c16c2fa02d4d1a0aeef06f87cc Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sat, 3 Dec 2022 14:14:15 +0800 Subject: [PATCH 190/250] refactor: adjust config --- common/net/tls.go | 18 +++++++++++ config/config.go | 79 +++++++++++++++++++++++---------------------- docs/config.yaml | 39 ++++++++++++---------- hub/hub.go | 10 ++---- hub/route/server.go | 56 ++++++++++++++++---------------- 5 files changed, 110 insertions(+), 92 deletions(-) create mode 100644 common/net/tls.go diff --git a/common/net/tls.go b/common/net/tls.go new file mode 100644 index 00000000..65391666 --- /dev/null +++ b/common/net/tls.go @@ -0,0 +1,18 @@ +package net + +import ( + "crypto/tls" + "fmt" +) +func ParseCert(certificate,privateKey string) (tls.Certificate, error) { + cert, painTextErr := tls.X509KeyPair([]byte(certificate), []byte(privateKey)) + if painTextErr == nil { + return cert, nil + } + + cert, loadErr := tls.LoadX509KeyPair(certificate, privateKey) + if loadErr != nil { + return tls.Certificate{}, fmt.Errorf("parse certificate failed,maybe format error:%s, or path error: %s", painTextErr.Error(), loadErr.Error()) + } + return cert, nil +} \ No newline at end of file diff --git a/config/config.go b/config/config.go index 39b75e54..1d80a3a5 100644 --- a/config/config.go +++ b/config/config.go @@ -77,12 +77,10 @@ type Inbound struct { // Controller config type Controller struct { - ExternalController string `json:"-"` - ExternalUI string `json:"-"` - Secret string `json:"-"` - TLSPort int `json:"-"` - Cert string `json:"-"` - PrivateKey string `json:"-"` + ExternalController string `json:"-"` + ExternalControllerTLS string `json:"-"` + ExternalUI string `json:"-"` + Secret string `json:"-"` } // DNS config @@ -130,6 +128,11 @@ type TuicServer struct { MaxUdpRelayPacketSize int `yaml:"max-udp-relay-packet-size" json:"max-udp-relay-packet-size,omitempty"` } +type TLS struct { + Certificate string `yaml:"certificate"` + PrivateKey string `yaml:"private-key"` +} + func (t TuicServer) String() string { b, _ := json.Marshal(t) return string(b) @@ -230,12 +233,10 @@ type Sniffer struct { ParsePureIp bool } + // Experimental config type Experimental struct { Fingerprints []string `yaml:"fingerprints"` - TLSPort int `yaml:"tls-port,omitempty"` - Cert string `yaml:"cert,omitempty"` - PrivateKey string `yaml:"private-key,omitempty"` } // Config is clash config manager @@ -254,6 +255,7 @@ type Config struct { RuleProviders map[string]providerTypes.RuleProvider Tunnels []Tunnel Sniffer *Sniffer + TLS *TLS } type RawDNS struct { @@ -381,31 +383,32 @@ func (t *Tunnel) UnmarshalYAML(unmarshal func(any) error) error { } 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"` - ShadowSocksConfig string `yaml:"ss-config"` - VmessConfig string `yaml:"vmess-config"` - InboundTfo bool `yaml:"inbound-tfo"` - Authentication []string `yaml:"authentication"` - 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"` - ExternalController string `yaml:"external-controller"` - ExternalUI string `yaml:"external-ui"` - Secret string `yaml:"secret"` - Interface string `yaml:"interface-name"` - RoutingMark int `yaml:"routing-mark"` - Tunnels []Tunnel `yaml:"tunnels"` - GeodataMode bool `yaml:"geodata-mode"` - GeodataLoader string `yaml:"geodata-loader"` - TCPConcurrent bool `yaml:"tcp-concurrent" json:"tcp-concurrent"` - EnableProcess bool `yaml:"enable-process" json:"enable-process"` + Port int `yaml:"port"` + SocksPort int `yaml:"socks-port"` + RedirPort int `yaml:"redir-port"` + TProxyPort int `yaml:"tproxy-port"` + MixedPort int `yaml:"mixed-port"` + ShadowSocksConfig string `yaml:"ss-config"` + VmessConfig string `yaml:"vmess-config"` + InboundTfo bool `yaml:"inbound-tfo"` + Authentication []string `yaml:"authentication"` + 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"` + ExternalController string `yaml:"external-controller"` + ExternalControllerTLS string `yaml:"external-controller-tls"` + ExternalUI string `yaml:"external-ui"` + Secret string `yaml:"secret"` + Interface string `yaml:"interface-name"` + RoutingMark int `yaml:"routing-mark"` + Tunnels []Tunnel `yaml:"tunnels"` + GeodataMode bool `yaml:"geodata-mode"` + GeodataLoader string `yaml:"geodata-loader"` + TCPConcurrent bool `yaml:"tcp-concurrent" json:"tcp-concurrent"` + EnableProcess bool `yaml:"enable-process" json:"enable-process"` Sniffer RawSniffer `yaml:"sniffer"` ProxyProvider map[string]map[string]any `yaml:"proxy-providers"` @@ -423,6 +426,7 @@ type RawConfig struct { ProxyGroup []map[string]any `yaml:"proxy-groups"` Rule []string `yaml:"rules"` SubRules map[string][]string `yaml:"sub-rules"` + RawTLS TLS `yaml:"tls"` } type RawGeoXUrl struct { @@ -572,6 +576,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { config.Experimental = &rawCfg.Experimental config.Profile = &rawCfg.Profile config.IPTables = &rawCfg.IPTables + config.TLS=&rawCfg.RawTLS general, err := parseGeneral(rawCfg) if err != nil { @@ -640,6 +645,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { return nil, err } + elapsedTime := time.Since(startTime) / time.Millisecond // duration in ms log.Infoln("Initial configuration complete, total time: %dms", elapsedTime) //Segment finished in xxm @@ -652,7 +658,6 @@ func parseGeneral(cfg *RawConfig) (*General, error) { // checkout externalUI exist if externalUI != "" { externalUI = C.Path.Resolve(externalUI) - if _, err := os.Stat(externalUI); os.IsNotExist(err) { return nil, fmt.Errorf("external-ui: %s not exist", externalUI) } @@ -675,9 +680,7 @@ func parseGeneral(cfg *RawConfig) (*General, error) { ExternalController: cfg.ExternalController, ExternalUI: cfg.ExternalUI, Secret: cfg.Secret, - TLSPort: cfg.Experimental.TLSPort, - Cert: cfg.Experimental.Cert, - PrivateKey: cfg.Experimental.PrivateKey, + ExternalControllerTLS: cfg.ExternalControllerTLS, }, UnifiedDelay: cfg.UnifiedDelay, Mode: cfg.Mode, diff --git a/docs/config.yaml b/docs/config.yaml index 78e9f025..e4bb5427 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -16,7 +16,7 @@ log-level: debug # 日志等级 silent/error/warning/info/debug ipv6: true # 开启 IPv6 总开关,关闭阻断所有 IPv6 链接和屏蔽 DNS 请求 AAAA 记录 external-controller: 0.0.0.0:9093 # RESTful API 监听地址 - +external-controller-tls: 0.0.0.0:9443 # RESTful API HTTPS 监听地址,需要配置 tls 部分配置文件 # secret: "123456" # `Authorization: Bearer ${secret}` # tcp-concurrent: true # TCP并发连接所有IP, 将使用最快握手的TCP @@ -51,29 +51,29 @@ tun: - 0.0.0.0/1 - 128.0.0.0/1 inet6_route_address: # 启用 auto_route 时使用自定义路由而不是默认路由 - - '::/1' - - '8000::/1' + - "::/1" + - "8000::/1" # endpoint_independent_nat: false # 启用独立于端点的 NAT # include_uid: # UID 规则仅在 Linux 下被支持,并且需要 auto_route - # - 0 + # - 0 # include_uid_range: # 限制被路由的的用户范围 - # - 1000-99999 + # - 1000-99999 # exclude_uid: # 排除路由的的用户 - #- 1000 + #- 1000 # exclude_uid_range: # 排除路由的的用户范围 - # - 1000-99999 - + # - 1000-99999 + # Android 用户和应用规则仅在 Android 下被支持 # 并且需要 auto_route - + # include_android_user: # 限制被路由的 Android 用户 - # - 0 - # - 10 + # - 0 + # - 10 # include_package: # 限制被路由的 Android 应用包名 - # - com.android.chrome + # - com.android.chrome # exclude_package: # 排除被路由的 Android 应用包名 - # - com.android.captiveportallogin - + # - com.android.captiveportallogin + #ebpf配置 ebpf: auto-redir: # redirect 模式,仅支持 TCP @@ -219,7 +219,8 @@ proxies: server: server port: 443 cipher: chacha20-ietf-poly1305 - password: "password" + password: + "password" # udp: true # udp-over-tcp: false # ip-version: ipv4 # 设置节点使用 IP 版本,可选:dual,ipv4,ipv6,ipv4-prefer,ipv6-prefer。默认使用 dual @@ -449,7 +450,7 @@ proxies: path: "/" headers: Host: example.com - + #hysteria - name: "hysteria" type: hysteria @@ -468,7 +469,7 @@ proxies: # recv_window_conn: 12582912 # 将会在未来某个时候删除 # recv-window-conn: 12582912 # recv_window: 52428800 # 将会在未来某个时候删除 - # recv-window: 52428800 + # recv-window: 52428800 # ca: "./my.ca" # ca_str: "xyz" # 将会在未来某个时候删除 # ca-str: "xyz" @@ -657,3 +658,7 @@ sub-rules: - IP-CIDR,1.1.1.1/32,REJECT - IP-CIDR,8.8.8.8/32,ss1 - DOMAIN,dns.alidns.com,REJECT + +tls: + certificate: string # 证书 PEM 格式,或者 证书的路径 + private-key: string # 证书对应的私钥 PEM 格式,或者私钥路径 diff --git a/hub/hub.go b/hub/hub.go index 216a0c68..ee18e70a 100644 --- a/hub/hub.go +++ b/hub/hub.go @@ -1,8 +1,6 @@ package hub import ( - "errors" - "github.com/Dreamacro/clash/config" "github.com/Dreamacro/clash/hub/executor" "github.com/Dreamacro/clash/hub/route" @@ -44,12 +42,8 @@ func Parse(options ...Option) error { } if cfg.General.ExternalController != "" { - if cfg.General.TLSPort != 0 && (len(cfg.General.PrivateKey) == 0 || len(cfg.General.Cert) == 0) { - return errors.New("Must be provided certificates and keys, for tls controller") - } - - go route.Start(cfg.General.ExternalController, cfg.General.Secret, cfg.General.TLSPort, - cfg.General.Cert, cfg.General.PrivateKey) + go route.Start(cfg.General.ExternalController,cfg.General.ExternalControllerTLS, + cfg.General.Secret,cfg.TLS.Certificate,cfg.TLS.PrivateKey) } executor.ApplyConfig(cfg, true) diff --git a/hub/route/server.go b/hub/route/server.go index 2b449c2d..00a33a08 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -4,15 +4,14 @@ import ( "bytes" "crypto/tls" "encoding/json" - "net" "net/http" - "strconv" "strings" "time" "github.com/Dreamacro/clash/adapter/inbound" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" + CN "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/tunnel/statistic" "github.com/go-chi/chi/v5" @@ -43,7 +42,8 @@ func SetUIPath(path string) { uiPath = C.Path.Resolve(path) } -func Start(addr string, secret string, tlsPort int, cert string, privateKey string) { +func Start(addr string, tlsAddr string, secret string, + certificat, privateKey string) { if serverAddr != "" { return } @@ -58,7 +58,6 @@ func Start(addr string, secret string, tlsPort int, cert string, privateKey stri AllowedHeaders: []string{"Content-Type", "Authorization"}, MaxAge: 300, }) - r.Use() r.Use(corsM.Handler) r.Group(func(r chi.Router) { r.Use(authentication) @@ -85,31 +84,31 @@ func Start(addr string, secret string, tlsPort int, cert string, privateKey stri }) }) } - if tlsPort >0 { + + if len(tlsAddr) > 0 { go func() { - if host, _, err := net.SplitHostPort(addr); err != nil { - log.Errorln("External controller tls serve error,%s", err) - } else { - l, err := inbound.Listen("tcp", net.JoinHostPort(host, strconv.Itoa(tlsPort))) - if err != nil { - log.Errorln("External controller tls listen error: %s", err) - return - } - serverAddr = l.Addr().String() - log.Infoln("RESTful API tls listening at: %s", serverAddr) - certificate, err := tls.X509KeyPair([]byte(cert), []byte(privateKey)) - if err != nil { - log.Errorln("External controller tls sevre error,%s", err) - } - tlsServe := &http.Server{ - Handler: r, - TLSConfig: &tls.Config{ - Certificates: []tls.Certificate{certificate}, - }, - } - if err = tlsServe.ServeTLS(l, "", ""); err != nil { - log.Errorln("External controller tls serve error: %s", err) - } + c, err := CN.ParseCert(certificat, privateKey) + if err != nil { + log.Errorln("External controller tls listen error: %s", err) + return + } + + l, err := inbound.Listen("tcp", tlsAddr) + if err != nil { + log.Errorln("External controller tls listen error: %s", err) + return + } + + serverAddr = l.Addr().String() + log.Infoln("RESTful API tls listening at: %s", serverAddr) + tlsServe := &http.Server{ + Handler: r, + TLSConfig: &tls.Config{ + Certificates: []tls.Certificate{c}, + }, + } + if err = tlsServe.ServeTLS(l, "", ""); err != nil { + log.Errorln("External controller tls serve error: %s", err) } }() } @@ -126,7 +125,6 @@ func Start(addr string, secret string, tlsPort int, cert string, privateKey stri log.Errorln("External controller serve error: %s", err) } - } func authentication(next http.Handler) http.Handler { From 4f75201a987a602e0d2ebc7fc156b782167f68c2 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sun, 4 Dec 2022 13:37:14 +0800 Subject: [PATCH 191/250] feat: add linsters --- adapter/inbound/http.go | 6 + adapter/inbound/https.go | 6 + adapter/inbound/packet.go | 27 ++-- adapter/inbound/socket.go | 11 +- config/config.go | 203 ++++++++------------------- constant/listener.go | 26 ++++ constant/metadata.go | 32 +++-- docs/config.yaml | 33 +++++ hub/executor/executor.go | 32 +++-- hub/route/configs.go | 12 +- listener/autoredir/tcp.go | 10 +- listener/http/client.go | 4 +- listener/http/proxy.go | 8 +- listener/http/server.go | 14 +- listener/http/upgrade.go | 4 +- listener/inbound/base.go | 66 +++++++++ listener/inbound/http.go | 52 +++++++ listener/inbound/mixed.go | 79 +++++++++++ listener/inbound/redir.go | 53 +++++++ listener/inbound/socks.go | 81 +++++++++++ listener/inbound/tproxy.go | 84 +++++++++++ listener/listener.go | 40 +++--- listener/mixed/mixed.go | 33 +++-- listener/parse.go | 50 +++++++ listener/redir/tcp.go | 15 +- listener/shadowsocks/tcp.go | 2 +- listener/shadowsocks/udp.go | 4 +- listener/sing/sing.go | 2 +- listener/sing_shadowsocks/server.go | 2 +- listener/sing_tun/server.go | 16 +-- listener/sing_tun/tun.go | 85 +++++++++++ listener/sing_vmess/server.go | 2 +- listener/socks/tcp.go | 24 ++-- listener/socks/udp.go | 16 ++- listener/tproxy/tproxy.go | 14 +- listener/tproxy/udp.go | 20 ++- listener/tuic/server.go | 28 +++- listener/tunnel/tunnel.go | 69 +++++++++ listener/tunnel/udp.go | 4 +- rules/logic/and.go | 2 +- rules/logic/common.go | 4 +- rules/logic/not.go | 2 +- rules/logic/or.go | 2 +- rules/logic/sub_rules.go | 14 +- rules/parser.go | 2 +- rules/provider/classical_strategy.go | 2 +- rules/provider/parse.go | 2 +- rules/provider/provider.go | 4 +- tunnel/tunnel.go | 21 ++- 49 files changed, 1018 insertions(+), 306 deletions(-) create mode 100644 listener/inbound/base.go create mode 100644 listener/inbound/http.go create mode 100644 listener/inbound/mixed.go create mode 100644 listener/inbound/redir.go create mode 100644 listener/inbound/socks.go create mode 100644 listener/inbound/tproxy.go create mode 100644 listener/parse.go create mode 100644 listener/sing_tun/tun.go create mode 100644 listener/tunnel/tunnel.go diff --git a/adapter/inbound/http.go b/adapter/inbound/http.go index 7ef880d1..4557d470 100644 --- a/adapter/inbound/http.go +++ b/adapter/inbound/http.go @@ -10,9 +10,15 @@ import ( // NewHTTP receive normal http request and return HTTPContext func NewHTTP(target socks5.Addr, source net.Addr, conn net.Conn) *context.ConnContext { + return NewHTTPWithInfos(target, source, conn, "", "") +} + +func NewHTTPWithInfos(target socks5.Addr, source net.Addr, conn net.Conn, inName, preferRulesName string) *context.ConnContext { metadata := parseSocksAddr(target) metadata.NetWork = C.TCP metadata.Type = C.HTTP + metadata.InName = inName + metadata.PreferRulesName = preferRulesName if ip, port, err := parseAddr(source.String()); err == nil { metadata.SrcIP = ip metadata.SrcPort = port diff --git a/adapter/inbound/https.go b/adapter/inbound/https.go index 53ad46b2..882802ef 100644 --- a/adapter/inbound/https.go +++ b/adapter/inbound/https.go @@ -10,8 +10,14 @@ import ( // NewHTTPS receive CONNECT request and return ConnContext func NewHTTPS(request *http.Request, conn net.Conn) *context.ConnContext { + return NewHTTPSWithInfos(request, conn, "", "") +} + +func NewHTTPSWithInfos(request *http.Request, conn net.Conn, inName, preferRulesName string) *context.ConnContext { metadata := parseHTTPAddr(request) metadata.Type = C.HTTPS + metadata.PreferRulesName = preferRulesName + metadata.InName = inName if ip, port, err := parseAddr(conn.RemoteAddr().String()); err == nil { metadata.SrcIP = ip metadata.SrcPort = port diff --git a/adapter/inbound/packet.go b/adapter/inbound/packet.go index 687e1089..1b377d36 100644 --- a/adapter/inbound/packet.go +++ b/adapter/inbound/packet.go @@ -5,22 +5,14 @@ import ( "github.com/Dreamacro/clash/transport/socks5" ) -// PacketAdapter is a UDP Packet adapter for socks/redir/tun -type PacketAdapter struct { - C.UDPPacket - metadata *C.Metadata -} -// Metadata returns destination metadata -func (s *PacketAdapter) Metadata() *C.Metadata { - return s.metadata -} -// NewPacket is PacketAdapter generator -func NewPacket(target socks5.Addr, packet C.UDPPacket, source C.Type) *PacketAdapter { +func NewPacketWithInfos(target socks5.Addr, packet C.UDPPacket, source C.Type, inName , preferRulesName string) *C.PacketAdapter { metadata := parseSocksAddr(target) metadata.NetWork = C.UDP metadata.Type = source + metadata.InName = inName + metadata.PreferRulesName = preferRulesName if ip, port, err := parseAddr(packet.LocalAddr().String()); err == nil { metadata.SrcIP = ip metadata.SrcPort = port @@ -32,8 +24,13 @@ func NewPacket(target socks5.Addr, packet C.UDPPacket, source C.Type) *PacketAda } } - return &PacketAdapter{ - UDPPacket: packet, - metadata: metadata, - } + return C.NewPacketAdapter( + packet, + metadata, + ) +} + +// NewPacket is PacketAdapter generator +func NewPacket(target socks5.Addr, packet C.UDPPacket, source C.Type) *C.PacketAdapter { + return NewPacketWithInfos(target, packet, source, "", "") } diff --git a/adapter/inbound/socket.go b/adapter/inbound/socket.go index 63231611..6bcdbced 100644 --- a/adapter/inbound/socket.go +++ b/adapter/inbound/socket.go @@ -9,12 +9,14 @@ import ( "github.com/Dreamacro/clash/transport/socks5" ) -// NewSocket receive TCP inbound and return ConnContext -func NewSocket(target socks5.Addr, conn net.Conn, source C.Type) *context.ConnContext { +func NewSocketWithInfos(target socks5.Addr, conn net.Conn, source C.Type, inName , preferRulesName string) *context.ConnContext { metadata := parseSocksAddr(target) metadata.NetWork = C.TCP metadata.Type = source + metadata.PreferRulesName = preferRulesName + metadata.InName = inName remoteAddr := conn.RemoteAddr() + // Filter when net.Addr interface is nil if remoteAddr != nil { if ip, port, err := parseAddr(remoteAddr.String()); err == nil { @@ -34,6 +36,11 @@ func NewSocket(target socks5.Addr, conn net.Conn, source C.Type) *context.ConnCo return context.NewConnContext(conn, metadata) } +// NewSocket receive TCP inbound and return ConnContext +func NewSocket(target socks5.Addr, conn net.Conn, source C.Type) *context.ConnContext { + return NewSocketWithInfos(target, conn, source, "", "") +} + func NewInner(conn net.Conn, dst string, host string) *context.ConnContext { metadata := &C.Metadata{} metadata.NetWork = C.TCP diff --git a/config/config.go b/config/config.go index 1d80a3a5..5ac357b2 100644 --- a/config/config.go +++ b/config/config.go @@ -15,9 +15,11 @@ import ( "time" "github.com/Dreamacro/clash/common/utils" + "github.com/Dreamacro/clash/listener/sing_tun" + "github.com/Dreamacro/clash/listener/tunnel" R "github.com/Dreamacro/clash/rules" RP "github.com/Dreamacro/clash/rules/provider" - + L "github.com/Dreamacro/clash/listener" "github.com/Dreamacro/clash/adapter" "github.com/Dreamacro/clash/adapter/outbound" "github.com/Dreamacro/clash/adapter/outboundgroup" @@ -30,13 +32,11 @@ import ( "github.com/Dreamacro/clash/component/trie" C "github.com/Dreamacro/clash/constant" providerTypes "github.com/Dreamacro/clash/constant/provider" - "github.com/Dreamacro/clash/constant/sniffer" snifferTypes "github.com/Dreamacro/clash/constant/sniffer" "github.com/Dreamacro/clash/dns" "github.com/Dreamacro/clash/log" T "github.com/Dreamacro/clash/tunnel" - "github.com/samber/lo" "gopkg.in/yaml.v3" ) @@ -149,11 +149,11 @@ type Tun struct { RedirectToTun []string `yaml:"-" json:"-"` MTU uint32 `yaml:"mtu" json:"mtu,omitempty"` - Inet4Address []ListenPrefix `yaml:"inet4-address" json:"inet4-address,omitempty"` - Inet6Address []ListenPrefix `yaml:"inet6-address" json:"inet6-address,omitempty"` + Inet4Address []sing_tun.ListenPrefix `yaml:"inet4-address" json:"inet4-address,omitempty"` + Inet6Address []sing_tun.ListenPrefix `yaml:"inet6-address" json:"inet6-address,omitempty"` StrictRoute bool `yaml:"strict-route" json:"strict-route,omitempty"` - Inet4RouteAddress []ListenPrefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"` - Inet6RouteAddress []ListenPrefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"` + Inet4RouteAddress []sing_tun.ListenPrefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"` + Inet6RouteAddress []sing_tun.ListenPrefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"` IncludeUID []uint32 `yaml:"include-uid" json:"include-uid,omitempty"` IncludeUIDRange []string `yaml:"include-uid-range" json:"include-uid-range,omitempty"` ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"` @@ -165,56 +165,6 @@ type Tun struct { UDPTimeout int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"` } -type ListenPrefix netip.Prefix - -func (p ListenPrefix) MarshalJSON() ([]byte, error) { - prefix := netip.Prefix(p) - if !prefix.IsValid() { - return json.Marshal(nil) - } - return json.Marshal(prefix.String()) -} - -func (p ListenPrefix) MarshalYAML() (interface{}, error) { - prefix := netip.Prefix(p) - if !prefix.IsValid() { - return nil, nil - } - return prefix.String(), nil -} - -func (p *ListenPrefix) UnmarshalJSON(bytes []byte) error { - var value string - err := json.Unmarshal(bytes, &value) - if err != nil { - return err - } - prefix, err := netip.ParsePrefix(value) - if err != nil { - return err - } - *p = ListenPrefix(prefix) - return nil -} - -func (p *ListenPrefix) UnmarshalYAML(node *yaml.Node) error { - var value string - err := node.Decode(&value) - if err != nil { - return err - } - prefix, err := netip.ParsePrefix(value) - if err != nil { - return err - } - *p = ListenPrefix(prefix) - return nil -} - -func (p ListenPrefix) Build() netip.Prefix { - return netip.Prefix(p) -} - // IPTables config type IPTables struct { Enable bool `yaml:"enable" json:"enable"` @@ -224,7 +174,7 @@ type IPTables struct { type Sniffer struct { Enable bool - Sniffers []sniffer.Type + Sniffers []snifferTypes.Type Reverses *trie.DomainTrie[struct{}] ForceDomain *trie.DomainTrie[struct{}] SkipDomain *trie.DomainTrie[struct{}] @@ -233,7 +183,6 @@ type Sniffer struct { ParsePureIp bool } - // Experimental config type Experimental struct { Fingerprints []string `yaml:"fingerprints"` @@ -248,12 +197,13 @@ type Config struct { Hosts *trie.DomainTrie[netip.Addr] Profile *Profile Rules []C.Rule - SubRules *map[string][]C.Rule + SubRules map[string][]C.Rule Users []auth.AuthUser Proxies map[string]C.Proxy + Listeners map[string]C.NewListener Providers map[string]providerTypes.ProxyProvider RuleProviders map[string]providerTypes.RuleProvider - Tunnels []Tunnel + Tunnels []tunnel.Tunnel Sniffer *Sniffer TLS *TLS } @@ -294,10 +244,10 @@ type RawTun struct { MTU uint32 `yaml:"mtu" json:"mtu,omitempty"` //Inet4Address []ListenPrefix `yaml:"inet4-address" json:"inet4_address,omitempty"` - Inet6Address []ListenPrefix `yaml:"inet6-address" json:"inet6_address,omitempty"` + Inet6Address []sing_tun.ListenPrefix `yaml:"inet6-address" json:"inet6_address,omitempty"` StrictRoute bool `yaml:"strict-route" json:"strict_route,omitempty"` - Inet4RouteAddress []ListenPrefix `yaml:"inet4_route_address" json:"inet4_route_address,omitempty"` - Inet6RouteAddress []ListenPrefix `yaml:"inet6_route_address" json:"inet6_route_address,omitempty"` + Inet4RouteAddress []sing_tun.ListenPrefix `yaml:"inet4_route_address" json:"inet4_route_address,omitempty"` + Inet6RouteAddress []sing_tun.ListenPrefix `yaml:"inet6_route_address" json:"inet6_route_address,omitempty"` IncludeUID []uint32 `yaml:"include-uid" json:"include_uid,omitempty"` IncludeUIDRange []string `yaml:"include-uid-range" json:"include_uid_range,omitempty"` ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude_uid,omitempty"` @@ -322,65 +272,6 @@ type RawTuicServer struct { MaxUdpRelayPacketSize int `yaml:"max-udp-relay-packet-size" json:"max-udp-relay-packet-size,omitempty"` } -type tunnel struct { - Network []string `yaml:"network"` - Address string `yaml:"address"` - Target string `yaml:"target"` - Proxy string `yaml:"proxy"` -} - -type Tunnel tunnel - -// UnmarshalYAML implements yaml.Unmarshaler -func (t *Tunnel) UnmarshalYAML(unmarshal func(any) error) error { - var tp string - if err := unmarshal(&tp); err != nil { - var inner tunnel - if err := unmarshal(&inner); err != nil { - return err - } - - *t = Tunnel(inner) - return nil - } - - // parse udp/tcp,address,target,proxy - parts := lo.Map(strings.Split(tp, ","), func(s string, _ int) string { - return strings.TrimSpace(s) - }) - if len(parts) != 3 && len(parts) != 4 { - return fmt.Errorf("invalid tunnel config %s", tp) - } - network := strings.Split(parts[0], "/") - - // validate network - for _, n := range network { - switch n { - case "tcp", "udp": - default: - return fmt.Errorf("invalid tunnel network %s", n) - } - } - - // validate address and target - address := parts[1] - target := parts[2] - for _, addr := range []string{address, target} { - if _, _, err := net.SplitHostPort(addr); err != nil { - return fmt.Errorf("invalid tunnel target or address %s", addr) - } - } - - *t = Tunnel(tunnel{ - Network: network, - Address: address, - Target: target, - }) - if len(parts) == 4 { - t.Proxy = parts[3] - } - return nil -} type RawConfig struct { Port int `yaml:"port"` @@ -404,7 +295,7 @@ type RawConfig struct { Secret string `yaml:"secret"` Interface string `yaml:"interface-name"` RoutingMark int `yaml:"routing-mark"` - Tunnels []Tunnel `yaml:"tunnels"` + Tunnels []tunnel.Tunnel `yaml:"tunnels"` GeodataMode bool `yaml:"geodata-mode"` GeodataLoader string `yaml:"geodata-loader"` TCPConcurrent bool `yaml:"tcp-concurrent" json:"tcp-concurrent"` @@ -426,7 +317,8 @@ type RawConfig struct { ProxyGroup []map[string]any `yaml:"proxy-groups"` Rule []string `yaml:"rules"` SubRules map[string][]string `yaml:"sub-rules"` - RawTLS TLS `yaml:"tls"` + RawTLS TLS `yaml:"tls"` + Listeners []map[string]any `yaml:"listeners"` } type RawGeoXUrl struct { @@ -492,7 +384,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { DNSHijack: []string{"0.0.0.0:53"}, // default hijack all dns query AutoRoute: true, AutoDetectInterface: true, - Inet6Address: []ListenPrefix{ListenPrefix(netip.MustParsePrefix("fdfe:dcba:9876::1/126"))}, + Inet6Address: []sing_tun.ListenPrefix{sing_tun.ListenPrefix(netip.MustParsePrefix("fdfe:dcba:9876::1/126"))}, }, TuicServer: RawTuicServer{ Enable: false, @@ -576,7 +468,7 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { config.Experimental = &rawCfg.Experimental config.Profile = &rawCfg.Profile config.IPTables = &rawCfg.IPTables - config.TLS=&rawCfg.RawTLS + config.TLS = &rawCfg.RawTLS general, err := parseGeneral(rawCfg) if err != nil { @@ -585,7 +477,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { config.General = general dialer.DefaultInterface.Store(config.General.Interface) - proxies, providers, err := parseProxies(rawCfg) if err != nil { return nil, err @@ -593,6 +484,12 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { config.Proxies = proxies config.Providers = providers + listener, err := parseListeners(rawCfg) + if err != nil { + return nil, err + } + config.Listeners = listener + subRules, ruleProviders, err := parseSubRules(rawCfg, proxies) if err != nil { return nil, err @@ -645,7 +542,6 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { return nil, err } - elapsedTime := time.Since(startTime) / time.Millisecond // duration in ms log.Infoln("Initial configuration complete, total time: %dms", elapsedTime) //Segment finished in xxm @@ -677,9 +573,9 @@ func parseGeneral(cfg *RawConfig) (*General, error) { InboundTfo: cfg.InboundTfo, }, Controller: Controller{ - ExternalController: cfg.ExternalController, - ExternalUI: cfg.ExternalUI, - Secret: cfg.Secret, + ExternalController: cfg.ExternalController, + ExternalUI: cfg.ExternalUI, + Secret: cfg.Secret, ExternalControllerTLS: cfg.ExternalControllerTLS, }, UnifiedDelay: cfg.UnifiedDelay, @@ -799,9 +695,27 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[ return proxies, providersMap, nil } -func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy) (subRules *map[string][]C.Rule, ruleProviders map[string]providerTypes.RuleProvider, err error) { +func parseListeners(cfg *RawConfig) (listeners map[string]C.NewListener, err error) { + listeners = make(map[string]C.NewListener) + for index, mapping := range cfg.Listeners { + listener, err := L.ParseListener(mapping) + if err != nil { + return nil, fmt.Errorf("proxy %d: %w", index, err) + } + + if _, exist := mapping[listener.Name()]; exist { + return nil, fmt.Errorf("listener %s is the duplicate name", listener.Name()) + } + + listeners[listener.Name()] = listener + + } + return +} + +func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy) (subRules map[string][]C.Rule, ruleProviders map[string]providerTypes.RuleProvider, err error) { ruleProviders = map[string]providerTypes.RuleProvider{} - subRules = &map[string][]C.Rule{} + subRules = map[string][]C.Rule{} log.Infoln("Geodata Loader mode: %s", geodata.LoaderName()) // parse rule provider for name, mapping := range cfg.RuleProvider { @@ -815,6 +729,9 @@ func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy) (subRules *map[st } for name, rawRules := range cfg.SubRules { + if len(name) == 0 { + return nil, nil, fmt.Errorf("sub-rule name is empty") + } var rules []C.Rule for idx, line := range rawRules { rawRule := trimArr(strings.Split(line, ",")) @@ -860,7 +777,7 @@ func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy) (subRules *map[st rules = append(rules, parsed) } - (*subRules)[name] = rules + subRules[name] = rules } if err = verifySubRule(subRules); err != nil { @@ -870,8 +787,8 @@ func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy) (subRules *map[st return } -func verifySubRule(subRules *map[string][]C.Rule) error { - for name := range *subRules { +func verifySubRule(subRules map[string][]C.Rule) error { + for name := range subRules { err := verifySubRuleCircularReferences(name, subRules, []string{}) if err != nil { return err @@ -880,7 +797,7 @@ func verifySubRule(subRules *map[string][]C.Rule) error { return nil } -func verifySubRuleCircularReferences(n string, subRules *map[string][]C.Rule, arr []string) error { +func verifySubRuleCircularReferences(n string, subRules map[string][]C.Rule, arr []string) error { isInArray := func(v string, array []string) bool { for _, c := range array { if v == c { @@ -891,9 +808,9 @@ func verifySubRuleCircularReferences(n string, subRules *map[string][]C.Rule, ar } arr = append(arr, n) - for i, rule := range (*subRules)[n] { + for i, rule := range subRules[n] { if rule.RuleType() == C.SubRules { - if _, ok := (*subRules)[rule.Adapter()]; !ok { + if _, ok := subRules[rule.Adapter()]; !ok { return fmt.Errorf("sub-rule[%d:%s] error: [%s] not found", i, n, rule.Adapter()) } if isInArray(rule.Adapter(), arr) { @@ -909,7 +826,7 @@ func verifySubRuleCircularReferences(n string, subRules *map[string][]C.Rule, ar return nil } -func parseRules(cfg *RawConfig, proxies map[string]C.Proxy, subRules *map[string][]C.Rule) ([]C.Rule, error) { +func parseRules(cfg *RawConfig, proxies map[string]C.Proxy, subRules map[string][]C.Rule) ([]C.Rule, error) { var rules []C.Rule rulesConfig := cfg.Rule @@ -948,7 +865,7 @@ func parseRules(cfg *RawConfig, proxies map[string]C.Proxy, subRules *map[string if _, ok := proxies[target]; !ok { if ruleName != "SUB-RULE" { return nil, fmt.Errorf("rules[%d] [%s] error: proxy [%s] not found", idx, line, target) - } else if _, ok = (*subRules)[target]; !ok { + } else if _, ok = subRules[target]; !ok { return nil, fmt.Errorf("rules[%d] [%s] error: sub-rule [%s] not found", idx, line, target) } } @@ -1315,7 +1232,7 @@ func parseTun(rawTun RawTun, general *General) error { RedirectToTun: rawTun.RedirectToTun, MTU: rawTun.MTU, - Inet4Address: []ListenPrefix{ListenPrefix(tunAddressPrefix)}, + Inet4Address: []sing_tun.ListenPrefix{sing_tun.ListenPrefix(tunAddressPrefix)}, Inet6Address: rawTun.Inet6Address, StrictRoute: rawTun.StrictRoute, Inet4RouteAddress: rawTun.Inet4RouteAddress, diff --git a/constant/listener.go b/constant/listener.go index 08a590bd..370bf892 100644 --- a/constant/listener.go +++ b/constant/listener.go @@ -13,3 +13,29 @@ type AdvanceListener interface { Config() string HandleConn(conn net.Conn, in chan<- ConnContext) } + +type NewListener interface { + Name() string + ReCreate(tcpIn chan<- ConnContext,udpIn chan<-*PacketAdapter) error + Close() error + Address() string + RawAddress() string +} + +// PacketAdapter is a UDP Packet adapter for socks/redir/tun +type PacketAdapter struct { + UDPPacket + metadata *Metadata +} + +func NewPacketAdapter(udppacket UDPPacket,metadata *Metadata)*PacketAdapter{ +return &PacketAdapter{ + udppacket, + metadata, +} +} + +// Metadata returns destination metadata +func (s *PacketAdapter) Metadata() *Metadata { + return s.metadata +} \ No newline at end of file diff --git a/constant/metadata.go b/constant/metadata.go index 4ab99f6b..0102ba74 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -117,21 +117,23 @@ func (t Type) MarshalJSON() ([]byte, error) { // Metadata is used to store connection address type Metadata struct { - NetWork NetWork `json:"network"` - Type Type `json:"type"` - SrcIP netip.Addr `json:"sourceIP"` - DstIP netip.Addr `json:"destinationIP"` - SrcPort string `json:"sourcePort"` - DstPort string `json:"destinationPort"` - InIP netip.Addr `json:"inboundIP"` - InPort string `json:"inboundPort"` - Host string `json:"host"` - DNSMode DNSMode `json:"dnsMode"` - Uid *uint32 `json:"uid"` - Process string `json:"process"` - ProcessPath string `json:"processPath"` - SpecialProxy string `json:"specialProxy"` - RemoteDst string `json:"remoteDestination"` + NetWork NetWork `json:"network"` + Type Type `json:"type"` + SrcIP netip.Addr `json:"sourceIP"` + DstIP netip.Addr `json:"destinationIP"` + SrcPort string `json:"sourcePort"` + DstPort string `json:"destinationPort"` + InIP netip.Addr `json:"inboundIP"` + InPort string `json:"inboundPort"` + Host string `json:"host"` + DNSMode DNSMode `json:"dnsMode"` + Uid *uint32 `json:"uid"` + Process string `json:"process"` + ProcessPath string `json:"processPath"` + SpecialProxy string `json:"specialProxy"` + RemoteDst string `json:"remoteDestination"` + InName string `jsson:"-"` + PreferRulesName string } func (m *Metadata) RemoteAddress() string { diff --git a/docs/config.yaml b/docs/config.yaml index e4bb5427..9507bbb8 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -662,3 +662,36 @@ sub-rules: tls: certificate: string # 证书 PEM 格式,或者 证书的路径 private-key: string # 证书对应的私钥 PEM 格式,或者私钥路径 + +# 流量入站 +listeners: + - name: socks5-in-1 + type: socks + port: 10808 + #listen: 0.0.0.0 # 默认监听 0.0.0.0 + # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules + # udp: false # 默认 true + - name: http-in-1 + type: http + port: 10809 + listen: 0.0.0.0 + # rule: sub-rule + - name: mixed-in-1 + type: mixed # HTTP(S) 和 SOCKS 代理混合 + port: 10810 + listen: 0.0.0.0 + # rule: sub-rule + # udp: false # 默认 true + + - name: reidr-in-1 + type: redir + port: 10811 + listen: 0.0.0.0 + # rule: sub-rule + + - name: tproxy-in-1 + type: tproxy + port: 10812 + listen: 0.0.0.0 + # udp: false # 默认 true + # rule: sub-rule diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 650242af..4a18b3c4 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -8,7 +8,6 @@ import ( "sync" "github.com/Dreamacro/clash/adapter" - "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/adapter/outboundgroup" "github.com/Dreamacro/clash/component/auth" "github.com/Dreamacro/clash/component/dialer" @@ -26,8 +25,12 @@ import ( "github.com/Dreamacro/clash/dns" "github.com/Dreamacro/clash/listener" authStore "github.com/Dreamacro/clash/listener/auth" + "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/listener/inner" + "github.com/Dreamacro/clash/listener/sing_tun" "github.com/Dreamacro/clash/listener/tproxy" + "github.com/Dreamacro/clash/listener/tuic" + T "github.com/Dreamacro/clash/listener/tunnel" "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/tunnel" ) @@ -77,7 +80,7 @@ func ApplyConfig(cfg *config.Config, force bool) { preUpdateExperimental(cfg) updateUsers(cfg.Users) updateProxies(cfg.Proxies, cfg.Providers) - updateRules(cfg.Rules, cfg.RuleProviders) + updateRules(cfg.Rules, cfg.SubRules, cfg.RuleProviders) updateSniffer(cfg.Sniffer) updateHosts(cfg.Hosts) initInnerTcp() @@ -86,6 +89,7 @@ func ApplyConfig(cfg *config.Config, force bool) { updateProfile(cfg) loadRuleProvider(cfg.RuleProviders) updateGeneral(cfg.General, force) + updateListeners(cfg.Listeners) updateIPTables(cfg) updateTun(cfg.General) updateExperimental(cfg) @@ -122,8 +126,8 @@ func GetGeneral() *config.General { LogLevel: log.Level(), IPv6: !resolver.DisableIPv6, GeodataLoader: G.LoaderName(), - Tun: listener.GetTunConf(), - TuicServer: listener.GetTuicConf(), + Tun: config.Tun(listener.GetTunConf()), + TuicServer: config.TuicServer(listener.GetTuicConf()), Interface: dialer.DefaultInterface.Load(), Sniffing: tunnel.IsSniffing(), TCPConcurrent: dialer.GetDial(), @@ -132,6 +136,16 @@ func GetGeneral() *config.General { return general } +func updateListeners(listeners map[string]C.NewListener) { + tcpIn := tunnel.TCPIn() + udpIn := tunnel.UDPIn() + for _, listener := range listeners { + if err := listener.ReCreate(tcpIn, udpIn); err != nil { + log.Errorln("Listener %s listen err: %s", listener.Name(), err.Error()) + } + } +} + func updateExperimental(c *config.Config) { runtime.GC() } @@ -203,8 +217,8 @@ func updateProxies(proxies map[string]C.Proxy, providers map[string]provider.Pro tunnel.UpdateProxies(proxies, providers) } -func updateRules(rules []C.Rule, ruleProviders map[string]provider.RuleProvider) { - tunnel.UpdateRules(rules, ruleProviders) +func updateRules(rules []C.Rule, subRules map[string][]C.Rule, ruleProviders map[string]provider.RuleProvider) { + tunnel.UpdateRules(rules, subRules, ruleProviders) } func loadProvider(pv provider.Provider) { @@ -267,7 +281,7 @@ func updateTun(general *config.General) { if general == nil { return } - listener.ReCreateTun(general.Tun, tunnel.TCPIn(), tunnel.UDPIn()) + listener.ReCreateTun(sing_tun.Tun(general.Tun), tunnel.TCPIn(), tunnel.UDPIn()) listener.ReCreateRedirToTun(general.Tun.RedirectToTun) } @@ -294,7 +308,7 @@ func updateSniffer(sniffer *config.Sniffer) { } } -func updateTunnels(tunnels []config.Tunnel) { +func updateTunnels(tunnels []T.Tunnel) { listener.PatchTunnel(tunnels, tunnel.TCPIn(), tunnel.UDPIn()) } @@ -353,7 +367,7 @@ func updateGeneral(general *config.General, force bool) { listener.ReCreateMixed(general.MixedPort, tcpIn, udpIn) listener.ReCreateShadowSocks(general.ShadowSocksConfig, tcpIn, udpIn) listener.ReCreateVmess(general.VmessConfig, tcpIn, udpIn) - listener.ReCreateTuic(general.TuicServer, tcpIn, udpIn) + listener.ReCreateTuic(tuic.TuicServer(general.TuicServer), tcpIn, udpIn) } func updateUsers(users []auth.AuthUser) { diff --git a/hub/route/configs.go b/hub/route/configs.go index 26b6eb6d..4b3ca328 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -13,6 +13,8 @@ import ( C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/hub/executor" P "github.com/Dreamacro/clash/listener" + "github.com/Dreamacro/clash/listener/sing_tun" + "github.com/Dreamacro/clash/listener/tuic" "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/tunnel" @@ -67,10 +69,10 @@ type tunSchema struct { MTU *uint32 `yaml:"mtu" json:"mtu,omitempty"` //Inet4Address *[]config.ListenPrefix `yaml:"inet4-address" json:"inet4-address,omitempty"` - Inet6Address *[]config.ListenPrefix `yaml:"inet6-address" json:"inet6-address,omitempty"` + Inet6Address *[]sing_tun.ListenPrefix `yaml:"inet6-address" json:"inet6-address,omitempty"` StrictRoute *bool `yaml:"strict-route" json:"strict-route,omitempty"` - Inet4RouteAddress *[]config.ListenPrefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"` - Inet6RouteAddress *[]config.ListenPrefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"` + Inet4RouteAddress *[]sing_tun.ListenPrefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"` + Inet6RouteAddress *[]sing_tun.ListenPrefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"` IncludeUID *[]uint32 `yaml:"include-uid" json:"include-uid,omitempty"` IncludeUIDRange *[]string `yaml:"include-uid-range" json:"include-uid-range,omitempty"` ExcludeUID *[]uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"` @@ -116,7 +118,7 @@ func pointerOrDefaultString(p *string, def string) string { return def } -func pointerOrDefaultTun(p *tunSchema, def config.Tun) config.Tun { +func pointerOrDefaultTun(p *tunSchema, def sing_tun.Tun) sing_tun.Tun { if p != nil { def.Enable = p.Enable if p.Device != nil { @@ -174,7 +176,7 @@ func pointerOrDefaultTun(p *tunSchema, def config.Tun) config.Tun { return def } -func pointerOrDefaultTuicServer(p *tuicServerSchema, def config.TuicServer) config.TuicServer { +func pointerOrDefaultTuicServer(p *tuicServerSchema, def tuic.TuicServer) tuic.TuicServer { if p != nil { def.Enable = p.Enable if p.Listen != nil { diff --git a/listener/autoredir/tcp.go b/listener/autoredir/tcp.go index efcd668b..936bffb5 100644 --- a/listener/autoredir/tcp.go +++ b/listener/autoredir/tcp.go @@ -14,6 +14,8 @@ type Listener struct { listener net.Listener addr string closed bool + name string + preferRulesName string lookupFunc func(netip.AddrPort) (socks5.Addr, error) } @@ -56,10 +58,14 @@ func (l *Listener) handleRedir(conn net.Conn, in chan<- C.ConnContext) { _ = conn.(*net.TCPConn).SetKeepAlive(true) - in <- inbound.NewSocket(target, conn, C.REDIR) + in <- inbound.NewSocketWithInfos(target, conn, C.REDIR,l.name,l.preferRulesName) } func New(addr string, in chan<- C.ConnContext) (*Listener, error) { + return NewWithInfos(addr,"DEFAULT-REDIR","",in) +} + +func NewWithInfos(addr ,name,preferRulesName string, in chan<- C.ConnContext) (*Listener, error) { l, err := net.Listen("tcp", addr) if err != nil { return nil, err @@ -67,6 +73,8 @@ func New(addr string, in chan<- C.ConnContext) (*Listener, error) { rl := &Listener{ listener: l, addr: addr, + name:name, + preferRulesName: preferRulesName, } go func() { diff --git a/listener/http/client.go b/listener/http/client.go index 873a9a3c..8b21f98b 100644 --- a/listener/http/client.go +++ b/listener/http/client.go @@ -12,7 +12,7 @@ import ( "github.com/Dreamacro/clash/transport/socks5" ) -func newClient(source net.Addr, in chan<- C.ConnContext) *http.Client { +func newClient(source net.Addr,name,preferRulesName string, in chan<- C.ConnContext) *http.Client { return &http.Client{ Transport: &http.Transport{ // from http.DefaultTransport @@ -32,7 +32,7 @@ func newClient(source net.Addr, in chan<- C.ConnContext) *http.Client { left, right := net.Pipe() - in <- inbound.NewHTTP(dstAddr, source, right) + in <- inbound.NewHTTPWithInfos(dstAddr, source, right,name,preferRulesName) return left, nil }, diff --git a/listener/http/proxy.go b/listener/http/proxy.go index 1c10732b..74d9d77e 100644 --- a/listener/http/proxy.go +++ b/listener/http/proxy.go @@ -14,8 +14,8 @@ import ( "github.com/Dreamacro/clash/log" ) -func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[string, bool]) { - client := newClient(c.RemoteAddr(), in) +func HandleConn(name, preferRulesName string, c net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[string, bool]) { + client := newClient(c.RemoteAddr(), name, preferRulesName, in) defer client.CloseIdleConnections() conn := N.NewBufferedConn(c) @@ -48,7 +48,7 @@ func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[strin break // close connection } - in <- inbound.NewHTTPS(request, conn) + in <- inbound.NewHTTPSWithInfos(request, conn, name, preferRulesName) return // hijack connection } @@ -61,7 +61,7 @@ func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[strin request.RequestURI = "" if isUpgradeRequest(request) { - handleUpgrade(conn, request, in) + handleUpgrade(name, preferRulesName, conn, request, in) return // hijack connection } diff --git a/listener/http/server.go b/listener/http/server.go index cf7948dd..fceafb68 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -12,6 +12,8 @@ type Listener struct { listener net.Listener addr string closed bool + name string + preferRulesName string } // RawAddress implements C.Listener @@ -31,10 +33,14 @@ func (l *Listener) Close() error { } func New(addr string, in chan<- C.ConnContext) (*Listener, error) { - return NewWithAuthenticate(addr, in, true) + return NewWithAuthenticate(addr,"DEFAULT-HTTP","", in, true) } -func NewWithAuthenticate(addr string, in chan<- C.ConnContext, authenticate bool) (*Listener, error) { +func NewWithInfos(addr ,name ,preferRulesName string,in chan<-C.ConnContext)(*Listener,error){ + return NewWithAuthenticate(addr,name,preferRulesName,in,true) +} + +func NewWithAuthenticate(addr,name,preferRulesName string, in chan<- C.ConnContext, authenticate bool) (*Listener, error) { l, err := inbound.Listen("tcp", addr) if err != nil { @@ -48,6 +54,8 @@ func NewWithAuthenticate(addr string, in chan<- C.ConnContext, authenticate bool hl := &Listener{ listener: l, + name: name, + preferRulesName: preferRulesName, addr: addr, } go func() { @@ -59,7 +67,7 @@ func NewWithAuthenticate(addr string, in chan<- C.ConnContext, authenticate bool } continue } - go HandleConn(conn, in, c) + go HandleConn(hl.name,hl.preferRulesName,conn, in, c) } }() diff --git a/listener/http/upgrade.go b/listener/http/upgrade.go index 9032166c..b0f1e389 100644 --- a/listener/http/upgrade.go +++ b/listener/http/upgrade.go @@ -25,7 +25,7 @@ func isUpgradeRequest(req *http.Request) bool { return false } -func handleUpgrade(conn net.Conn, request *http.Request, in chan<- C.ConnContext) { +func handleUpgrade(name,preferRulesName string,conn net.Conn, request *http.Request, in chan<- C.ConnContext) { defer conn.Close() removeProxyHeaders(request.Header) @@ -43,7 +43,7 @@ func handleUpgrade(conn net.Conn, request *http.Request, in chan<- C.ConnContext left, right := net.Pipe() - in <- inbound.NewHTTP(dstAddr, conn.RemoteAddr(), right) + in <- inbound.NewHTTPWithInfos(dstAddr, conn.RemoteAddr(), right,name,preferRulesName) var bufferedLeft *N.BufferedConn if request.TLS != nil { diff --git a/listener/inbound/base.go b/listener/inbound/base.go new file mode 100644 index 00000000..9fec28cd --- /dev/null +++ b/listener/inbound/base.go @@ -0,0 +1,66 @@ +package inbound + +import ( + "net" + "net/netip" + "strconv" + + C "github.com/Dreamacro/clash/constant" +) + +type Base struct { + name string + preferRulesName string + listenAddr netip.Addr + port int +} + +func NewBase(options *BaseOption) (*Base, error) { + if options.Listen == "" { + options.Listen = "0.0.0.0" + } + addr, err := netip.ParseAddr(options.Listen) + if err != nil { + return nil, err + } + return &Base{ + name: options.Name, + listenAddr: addr, + preferRulesName: options.PreferRulesName, + port: options.Port, + }, nil +} + +// Address implements constant.NewListener +func (b *Base) Address() string { + return b.RawAddress() +} + +// Close implements constant.NewListener +func (*Base) Close() error { + return nil +} + +// Name implements constant.NewListener +func (b *Base) Name() string { + return b.name +} + +// RawAddress implements constant.NewListener +func (b *Base) RawAddress() string { + return net.JoinHostPort(b.listenAddr.String(), strconv.Itoa(int(b.port))) +} + +// ReCreate implements constant.NewListener +func (*Base) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) error { + return nil +} + +type BaseOption struct { + Name string `inbound:"name"` + Listen string `inbound:"listen,omitempty"` + Port int `inbound:"port"` + PreferRulesName string `inbound:"rule,omitempty"` +} + +var _ C.NewListener = (*Base)(nil) diff --git a/listener/inbound/http.go b/listener/inbound/http.go new file mode 100644 index 00000000..1902109f --- /dev/null +++ b/listener/inbound/http.go @@ -0,0 +1,52 @@ +package inbound + +import ( + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/listener/http" + "github.com/Dreamacro/clash/log" +) + +type HTTPOption struct { + BaseOption +} +type HTTP struct { + *Base + l *http.Listener +} + +func NewHTTP(options *HTTPOption) (*HTTP, error) { + base, err := NewBase(&options.BaseOption) + if err != nil { + return nil, err + } + return &HTTP{ + Base: base, + }, nil +} + +// Address implements constant.NewListener +func (h *HTTP) Address() string { + return h.l.Address() +} + +// ReCreate implements constant.NewListener +func (h *HTTP) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) error { + var err error + _ = h.Close() + h.l, err = http.NewWithInfos(h.RawAddress(), h.name, h.preferRulesName, tcpIn) + if err != nil { + return err + } + log.Infoln("HTTP[%s] proxy listening at: %s", h.Name(), h.Address()) + return nil +} + +// Close implements constant.NewListener +func (h *HTTP) Close() error { + if h.l != nil { + return h.l.Close() + } + return nil +} + +var _ C.NewListener = (*HTTP)(nil) diff --git a/listener/inbound/mixed.go b/listener/inbound/mixed.go new file mode 100644 index 00000000..691b2503 --- /dev/null +++ b/listener/inbound/mixed.go @@ -0,0 +1,79 @@ +package inbound + +import ( + "fmt" + + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/log" + + "github.com/Dreamacro/clash/listener/mixed" + "github.com/Dreamacro/clash/listener/socks" +) + +type MixedOption struct { + BaseOption + UDP *bool `inbound:"udp,omitempty"` +} + +type Mixed struct { + *Base + l *mixed.Listener + lUDP *socks.UDPListener + udp bool +} + +func NewMixed(options *MixedOption) (*Mixed, error) { + base, err := NewBase(&options.BaseOption) + if err != nil { + return nil, err + } + return &Mixed{ + Base: base, + udp: options.UDP == nil || *options.UDP, + }, nil +} + +// Address implements constant.NewListener +func (m *Mixed) Address() string { + return m.l.Address() +} + +// ReCreate implements constant.NewListener +func (m *Mixed) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) error { + var err error + _ = m.Close() + m.l, err = mixed.NewWithInfos(m.RawAddress(), m.name, m.preferRulesName, tcpIn) + if err != nil { + return err + } + if m.udp { + m.lUDP, err = socks.NewUDPWithInfos(m.Address(), m.name, m.preferRulesName, udpIn) + if err != nil { + return err + } + } + log.Infoln("Mixed(http+socks)[%s] proxy listening at: %s", m.Name(), m.Address()) + return nil +} + +// Close implements constant.NewListener +func (m *Mixed) Close() error { + var err error + if m.l != nil { + if tcpErr := m.l.Close(); tcpErr != nil { + err = tcpErr + } + } + if m.udp && m.lUDP != nil { + if udpErr := m.lUDP.Close(); udpErr != nil { + if err == nil { + err = udpErr + } else { + return fmt.Errorf("close tcp err: %s, close udp err: %s", err.Error(), udpErr.Error()) + } + } + } + return err +} + +var _ C.NewListener = (*Mixed)(nil) diff --git a/listener/inbound/redir.go b/listener/inbound/redir.go new file mode 100644 index 00000000..baf6c0f7 --- /dev/null +++ b/listener/inbound/redir.go @@ -0,0 +1,53 @@ +package inbound + +import ( + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/listener/redir" + "github.com/Dreamacro/clash/log" +) + +type RedirOption struct { + BaseOption +} + +type Redir struct { + *Base + l *redir.Listener +} + +func NewRedir(options *RedirOption) (*Redir, error) { + base, err := NewBase(&options.BaseOption) + if err != nil { + return nil, err + } + return &Redir{ + Base: base, + }, nil +} + +// Address implements constant.NewListener +func (r *Redir) Address() string { + return r.l.Address() +} + +// ReCreate implements constant.NewListener +func (r *Redir) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) error { + var err error + _ = r.Close() + r.l, err = redir.NewWithInfos(r.Address(), r.name, r.preferRulesName, tcpIn) + if err != nil { + return err + } + log.Infoln("Redir[%s] proxy listening at: %s", r.Name(), r.Address()) + return nil +} + +// Close implements constant.NewListener +func (r *Redir) Close() error { + if r.l != nil { + r.l.Close() + } + return nil +} + +var _ C.NewListener = (*Redir)(nil) diff --git a/listener/inbound/socks.go b/listener/inbound/socks.go new file mode 100644 index 00000000..35e11297 --- /dev/null +++ b/listener/inbound/socks.go @@ -0,0 +1,81 @@ +package inbound + +import ( + "fmt" + "sync" + + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/listener/socks" + "github.com/Dreamacro/clash/log" +) + +type SocksOption struct { + BaseOption + UDP *bool `inbound:"udp,omitempty"` +} + +type Socks struct { + *Base + mux sync.Mutex + udp bool + stl *socks.Listener + sul *socks.UDPListener +} + +func NewSocks(options *SocksOption) (*Socks, error) { + base, err := NewBase(&options.BaseOption) + if err != nil { + return nil, err + } + return &Socks{ + Base: base, + udp: options.UDP == nil || *options.UDP, + }, nil +} + +// Close implements constant.NewListener +func (s *Socks) Close() error { + var err error + if s.stl != nil { + if tcpErr := s.stl.Close(); tcpErr != nil { + err = tcpErr + } + } + if s.udp && s.sul != nil { + if udpErr := s.sul.Close(); udpErr != nil { + if err == nil { + err = udpErr + } else { + return fmt.Errorf("close tcp err: %s, close udp err: %s", err.Error(), udpErr.Error()) + } + } + } + + return err +} + +// Address implements constant.NewListener +func (s *Socks) Address() string { + return s.stl.Address() +} + +// ReCreate implements constant.NewListener +func (s *Socks) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) error { + s.mux.Lock() + defer s.mux.Unlock() + var err error + _ = s.Close() + if s.stl, err = socks.NewWithInfos(s.RawAddress(), s.name, s.preferRulesName, tcpIn); err != nil { + return err + } + if s.udp { + if s.sul, err = socks.NewUDPWithInfos(s.RawAddress(), s.name, s.preferRulesName, udpIn); err != nil { + return err + } + } + + log.Infoln("SOCKS[%s] proxy listening at: %s", s.Name(), s.Address()) + return nil +} + +var _ C.NewListener = (*Socks)(nil) diff --git a/listener/inbound/tproxy.go b/listener/inbound/tproxy.go new file mode 100644 index 00000000..acca5c8e --- /dev/null +++ b/listener/inbound/tproxy.go @@ -0,0 +1,84 @@ +package inbound + +import ( + "fmt" + + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/listener/tproxy" + "github.com/Dreamacro/clash/log" +) + +type TProxyOption struct { + BaseOption + UDP *bool `inbound:"udp,omitempty"` +} + +type TProxy struct { + *Base + lUDP *tproxy.UDPListener + lTCP *tproxy.Listener + udp bool +} + +func NewTProxy(options *TProxyOption) (*TProxy, error) { + base, err := NewBase(&options.BaseOption) + if err != nil { + return nil, err + } + return &TProxy{ + Base: base, + udp: options.UDP == nil || *options.UDP, + }, nil + +} + +// Address implements constant.NewListener +func (t *TProxy) Address() string { + return t.lTCP.Address() +} + +// ReCreate implements constant.NewListener +func (t *TProxy) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) error { + var err error + _ = t.Close() + t.lTCP, err = tproxy.NewWithInfos(t.RawAddress(), t.name, t.preferRulesName, tcpIn) + if err != nil { + return err + } + if t.udp { + if t.lUDP != nil { + t.lUDP, err = tproxy.NewUDPWithInfos(t.Address(), t.name, t.preferRulesName, udpIn) + if err != nil { + return err + } + } + + } + log.Infoln("TProxy[%s] proxy listening at: %s", t.Name(), t.Address()) + return nil +} + +// Close implements constant.NewListener +func (t *TProxy) Close() error { + var tcpErr error + var udpErr error + if t.lTCP != nil { + tcpErr = t.lTCP.Close() + } + if t.lUDP != nil { + udpErr = t.lUDP.Close() + } + + if tcpErr != nil && udpErr != nil { + return fmt.Errorf("tcp close err: %s and udp close err: %s", tcpErr, udpErr) + } + if tcpErr != nil { + return tcpErr + } + if udpErr != nil { + return udpErr + } + return nil +} + +var _ C.NewListener = (*TProxy)(nil) diff --git a/listener/listener.go b/listener/listener.go index 4601b443..4bf82369 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -9,9 +9,7 @@ import ( "strings" "sync" - "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/component/ebpf" - "github.com/Dreamacro/clash/config" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/listener/autoredir" "github.com/Dreamacro/clash/listener/http" @@ -66,8 +64,8 @@ var ( autoRedirMux sync.Mutex tcMux sync.Mutex - LastTunConf config.Tun - LastTuicConf config.TuicServer + LastTunConf sing_tun.Tun + LastTuicConf tuic.TuicServer ) type Ports struct { @@ -80,18 +78,18 @@ type Ports struct { VmessConfig string `json:"vmess-config"` } -func GetTunConf() config.Tun { +func GetTunConf() sing_tun.Tun { if tunLister == nil { - return config.Tun{ + return sing_tun.Tun{ Enable: false, } } return tunLister.Config() } -func GetTuicConf() config.TuicServer { +func GetTuicConf() tuic.TuicServer { if tuicListener == nil { - return config.TuicServer{Enable: false} + return tuic.TuicServer{Enable: false} } return tuicListener.Config() } @@ -146,7 +144,7 @@ func ReCreateHTTP(port int, tcpIn chan<- C.ConnContext) { log.Infoln("HTTP proxy listening at: %s", httpListener.Address()) } -func ReCreateSocks(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { +func ReCreateSocks(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) { socksMux.Lock() defer socksMux.Unlock() @@ -205,7 +203,7 @@ func ReCreateSocks(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P log.Infoln("SOCKS proxy listening at: %s", socksListener.Address()) } -func ReCreateRedir(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { +func ReCreateRedir(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) { redirMux.Lock() defer redirMux.Unlock() @@ -251,7 +249,7 @@ func ReCreateRedir(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P log.Infoln("Redirect proxy listening at: %s", redirListener.Address()) } -func ReCreateShadowSocks(shadowSocksConfig string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { +func ReCreateShadowSocks(shadowSocksConfig string, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) { ssMux.Lock() defer ssMux.Unlock() @@ -291,7 +289,7 @@ func ReCreateShadowSocks(shadowSocksConfig string, tcpIn chan<- C.ConnContext, u return } -func ReCreateVmess(vmessConfig string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { +func ReCreateVmess(vmessConfig string, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) { vmessMux.Lock() defer vmessMux.Unlock() @@ -331,7 +329,7 @@ func ReCreateVmess(vmessConfig string, tcpIn chan<- C.ConnContext, udpIn chan<- return } -func ReCreateTuic(config config.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { +func ReCreateTuic(config tuic.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) { tuicMux.Lock() defer func() { LastTuicConf = config @@ -373,7 +371,7 @@ func ReCreateTuic(config config.TuicServer, tcpIn chan<- C.ConnContext, udpIn ch return } -func ReCreateTProxy(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { +func ReCreateTProxy(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) { tproxyMux.Lock() defer tproxyMux.Unlock() @@ -419,7 +417,7 @@ func ReCreateTProxy(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound. log.Infoln("TProxy server listening at: %s", tproxyListener.Address()) } -func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { +func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) { mixedMux.Lock() defer mixedMux.Unlock() @@ -474,7 +472,7 @@ func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P log.Infoln("Mixed(http+socks) proxy listening at: %s", mixedListener.Address()) } -func ReCreateTun(tunConf config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { +func ReCreateTun(tunConf sing_tun.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) { tunMux.Lock() defer func() { LastTunConf = tunConf @@ -538,7 +536,7 @@ func ReCreateRedirToTun(ifaceNames []string) { log.Infoln("Attached tc ebpf program to interfaces %v", tcProgram.RawNICs()) } -func ReCreateAutoRedir(ifaceNames []string, tcpIn chan<- C.ConnContext, _ chan<- *inbound.PacketAdapter) { +func ReCreateAutoRedir(ifaceNames []string, tcpIn chan<- C.ConnContext, _ chan<- *C.PacketAdapter) { autoRedirMux.Lock() defer autoRedirMux.Unlock() @@ -594,7 +592,7 @@ func ReCreateAutoRedir(ifaceNames []string, tcpIn chan<- C.ConnContext, _ chan<- log.Infoln("Auto redirect proxy listening at: %s, attached tc ebpf program to interfaces %v", autoRedirListener.Address(), autoRedirProgram.RawNICs()) } -func PatchTunnel(tunnels []config.Tunnel, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) { +func PatchTunnel(tunnels []tunnel.Tunnel, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) { tunnelMux.Lock() defer tunnelMux.Unlock() @@ -633,7 +631,7 @@ func PatchTunnel(tunnels []config.Tunnel, tcpIn chan<- C.ConnContext, udpIn chan newElm := lo.FlatMap( tunnels, - func(tunnel config.Tunnel, _ int) []addrProxy { + func(tunnel tunnel.Tunnel, _ int) []addrProxy { return lo.Map( tunnel.Network, func(network string, _ int) addrProxy { @@ -747,7 +745,7 @@ func genAddr(host string, port int, allowLan bool) string { return fmt.Sprintf("127.0.0.1:%d", port) } -func hasTunConfigChange(tunConf *config.Tun) bool { +func hasTunConfigChange(tunConf *sing_tun.Tun) bool { if LastTunConf.Enable != tunConf.Enable || LastTunConf.Device != tunConf.Device || LastTunConf.Stack != tunConf.Stack || @@ -835,5 +833,5 @@ func Cleanup(wait bool) { tunLister.Close() tunLister = nil } - LastTunConf = config.Tun{} + LastTunConf = sing_tun.Tun{} } diff --git a/listener/mixed/mixed.go b/listener/mixed/mixed.go index 0ee50ba4..fe42a14e 100644 --- a/listener/mixed/mixed.go +++ b/listener/mixed/mixed.go @@ -14,11 +14,12 @@ import ( ) type Listener struct { - listener net.Listener - addr string - - cache *cache.LruCache[string, bool] - closed bool + listener net.Listener + addr string + name string + preferRulesName string + cache *cache.LruCache[string, bool] + closed bool } // RawAddress implements C.Listener @@ -38,15 +39,21 @@ func (l *Listener) Close() error { } func New(addr string, in chan<- C.ConnContext) (*Listener, error) { + return NewWithInfos(addr, "DEFAULT-MIXED", "", in) +} + +func NewWithInfos(addr, name, preferRulesName string, in chan<- C.ConnContext) (*Listener, error) { l, err := inbound.Listen("tcp", addr) if err != nil { return nil, err } ml := &Listener{ - listener: l, - addr: addr, - cache: cache.New[string, bool](cache.WithAge[string, bool](30)), + listener: l, + addr: addr, + name: name, + preferRulesName: preferRulesName, + cache: cache.New[string, bool](cache.WithAge[string, bool](30)), } go func() { for { @@ -57,14 +64,14 @@ func New(addr string, in chan<- C.ConnContext) (*Listener, error) { } continue } - go handleConn(c, in, ml.cache) + go handleConn(ml.name, ml.preferRulesName, c, in, ml.cache) } }() return ml, nil } -func handleConn(conn net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[string, bool]) { +func handleConn(name, preferRulesName string, conn net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[string, bool]) { conn.(*net.TCPConn).SetKeepAlive(true) bufConn := N.NewBufferedConn(conn) @@ -75,10 +82,10 @@ func handleConn(conn net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[st switch head[0] { case socks4.Version: - socks.HandleSocks4(bufConn, in) + socks.HandleSocks4(name, preferRulesName, bufConn, in) case socks5.Version: - socks.HandleSocks5(bufConn, in) + socks.HandleSocks5(name, preferRulesName, bufConn, in) default: - http.HandleConn(bufConn, in, cache) + http.HandleConn(name, preferRulesName, bufConn, in, cache) } } diff --git a/listener/parse.go b/listener/parse.go new file mode 100644 index 00000000..026c7df6 --- /dev/null +++ b/listener/parse.go @@ -0,0 +1,50 @@ +package listener + +import ( + "fmt" + "strings" + + "github.com/Dreamacro/clash/common/structure" + C "github.com/Dreamacro/clash/constant" + IN "github.com/Dreamacro/clash/listener/inbound" +) + +var keyReplacer = strings.NewReplacer("_", "-") + +func ParseListener(mapping map[string]any) (C.NewListener, error) { + decoder := structure.NewDecoder(structure.Option{TagName: "inbound", WeaklyTypedInput: true, KeyReplacer: keyReplacer}) + proxyType, existType := mapping["type"].(string) + if !existType { + return nil, fmt.Errorf("missing type") + } + + var ( + listener C.NewListener + err error + ) + switch proxyType { + case "socks": + socksOption := &IN.SocksOption{} + decoder.Decode(mapping, socksOption) + listener, err = IN.NewSocks(socksOption) + case "http": + httpOption := &IN.HTTPOption{} + decoder.Decode(mapping, httpOption) + listener, err = IN.NewHTTP(httpOption) + case "tproxy": + tproxyOption := &IN.TProxyOption{} + decoder.Decode(mapping, tproxyOption) + listener, err = IN.NewTProxy(tproxyOption) + case "redir": + redirOption := &IN.RedirOption{} + decoder.Decode(mapping, redirOption) + listener, err = IN.NewRedir(redirOption) + case "mixed": + mixedOption := &IN.MixedOption{} + decoder.Decode(mapping, mixedOption) + listener, err = IN.NewMixed(mixedOption) + default: + return nil, fmt.Errorf("unsupport proxy type: %s", proxyType) + } + return listener, err +} diff --git a/listener/redir/tcp.go b/listener/redir/tcp.go index 15c98a8f..d55897cb 100644 --- a/listener/redir/tcp.go +++ b/listener/redir/tcp.go @@ -11,6 +11,8 @@ type Listener struct { listener net.Listener addr string closed bool + name string + preferRulesName string } // RawAddress implements C.Listener @@ -30,6 +32,10 @@ func (l *Listener) Close() error { } func New(addr string, in chan<- C.ConnContext) (*Listener, error) { + return NewWithInfos(addr,"DEFAULT-REDIR","",in) +} + +func NewWithInfos(addr,name,preferRulesName string, in chan<- C.ConnContext) (*Listener, error) { l, err := net.Listen("tcp", addr) if err != nil { return nil, err @@ -37,6 +43,8 @@ func New(addr string, in chan<- C.ConnContext) (*Listener, error) { rl := &Listener{ listener: l, addr: addr, + name: name, + preferRulesName: preferRulesName, } go func() { @@ -48,19 +56,18 @@ func New(addr string, in chan<- C.ConnContext) (*Listener, error) { } continue } - go handleRedir(c, in) + go handleRedir(rl.name,rl.preferRulesName,c, in) } }() return rl, nil } - -func handleRedir(conn net.Conn, in chan<- C.ConnContext) { +func handleRedir(name,preferRulesName string,conn net.Conn, in chan<- C.ConnContext) { target, err := parserPacket(conn) if err != nil { conn.Close() return } conn.(*net.TCPConn).SetKeepAlive(true) - in <- inbound.NewSocket(target, conn, C.REDIR) + in <- inbound.NewSocketWithInfos(target, conn, C.REDIR,name,preferRulesName) } diff --git a/listener/shadowsocks/tcp.go b/listener/shadowsocks/tcp.go index c37892bb..74ffca59 100644 --- a/listener/shadowsocks/tcp.go +++ b/listener/shadowsocks/tcp.go @@ -21,7 +21,7 @@ type Listener struct { var _listener *Listener -func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (*Listener, error) { +func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) (*Listener, error) { addr, cipher, password, err := ParseSSURL(config) if err != nil { return nil, err diff --git a/listener/shadowsocks/udp.go b/listener/shadowsocks/udp.go index efb29e41..5ea17c6b 100644 --- a/listener/shadowsocks/udp.go +++ b/listener/shadowsocks/udp.go @@ -17,7 +17,7 @@ type UDPListener struct { closed bool } -func NewUDP(addr string, pickCipher core.Cipher, in chan<- *inbound.PacketAdapter) (*UDPListener, error) { +func NewUDP(addr string, pickCipher core.Cipher, in chan<- *C.PacketAdapter) (*UDPListener, error) { l, err := net.ListenPacket("udp", addr) if err != nil { return nil, err @@ -53,7 +53,7 @@ func (l *UDPListener) Close() error { return l.packetConn.Close() } -func handleSocksUDP(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buf []byte, addr net.Addr) { +func handleSocksUDP(pc net.PacketConn, in chan<- *C.PacketAdapter, buf []byte, addr net.Addr) { tgtAddr := socks5.SplitAddr(buf) if tgtAddr == nil { // Unresolved UDP packet, return buffer to the pool diff --git a/listener/sing/sing.go b/listener/sing/sing.go index df55f100..4ff37f6a 100644 --- a/listener/sing/sing.go +++ b/listener/sing/sing.go @@ -24,7 +24,7 @@ const UDPTimeout = 5 * time.Minute type ListenerHandler struct { TcpIn chan<- C.ConnContext - UdpIn chan<- *inbound.PacketAdapter + UdpIn chan<- *C.PacketAdapter Type C.Type } diff --git a/listener/sing_shadowsocks/server.go b/listener/sing_shadowsocks/server.go index b7073e5d..ecfc1df7 100644 --- a/listener/sing_shadowsocks/server.go +++ b/listener/sing_shadowsocks/server.go @@ -32,7 +32,7 @@ type Listener struct { var _listener *Listener -func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (C.AdvanceListener, error) { +func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) (C.AdvanceListener, error) { addr, cipher, password, err := embedSS.ParseSSURL(config) if err != nil { return nil, err diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 9d010fd4..13437149 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -8,10 +8,8 @@ import ( "strconv" "strings" - "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/iface" - "github.com/Dreamacro/clash/config" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/listener/sing" "github.com/Dreamacro/clash/log" @@ -27,7 +25,7 @@ var InterfaceName = "Meta" type Listener struct { closed bool - options config.Tun + options Tun handler *ListenerHandler tunName string @@ -65,7 +63,7 @@ func CalculateInterfaceName(name string) (tunName string) { return } -func New(options config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (l *Listener, err error) { +func New(options Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) (l *Listener, err error) { tunName := options.Device if tunName == "" { tunName = CalculateInterfaceName(InterfaceName) @@ -163,12 +161,12 @@ func New(options config.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.P tunOptions := tun.Options{ Name: tunName, MTU: tunMTU, - Inet4Address: common.Map(options.Inet4Address, config.ListenPrefix.Build), - Inet6Address: common.Map(options.Inet6Address, config.ListenPrefix.Build), + Inet4Address: common.Map(options.Inet4Address, ListenPrefix.Build), + Inet6Address: common.Map(options.Inet6Address, ListenPrefix.Build), AutoRoute: options.AutoRoute, StrictRoute: options.StrictRoute, - Inet4RouteAddress: common.Map(options.Inet4RouteAddress, config.ListenPrefix.Build), - Inet6RouteAddress: common.Map(options.Inet6RouteAddress, config.ListenPrefix.Build), + Inet4RouteAddress: common.Map(options.Inet4RouteAddress, ListenPrefix.Build), + Inet6RouteAddress: common.Map(options.Inet6RouteAddress, ListenPrefix.Build), IncludeUID: includeUID, ExcludeUID: excludeUID, IncludeAndroidUser: options.IncludeAndroidUser, @@ -284,6 +282,6 @@ func (l *Listener) Close() { ) } -func (l *Listener) Config() config.Tun { +func (l *Listener) Config() Tun { return l.options } diff --git a/listener/sing_tun/tun.go b/listener/sing_tun/tun.go new file mode 100644 index 00000000..95ff61ba --- /dev/null +++ b/listener/sing_tun/tun.go @@ -0,0 +1,85 @@ +package sing_tun + +import ( + "encoding/json" + "net/netip" + + C "github.com/Dreamacro/clash/constant" + "gopkg.in/yaml.v3" +) + +type ListenPrefix netip.Prefix + +func (p ListenPrefix) MarshalJSON() ([]byte, error) { + prefix := netip.Prefix(p) + if !prefix.IsValid() { + return json.Marshal(nil) + } + return json.Marshal(prefix.String()) +} + +func (p ListenPrefix) MarshalYAML() (interface{}, error) { + prefix := netip.Prefix(p) + if !prefix.IsValid() { + return nil, nil + } + return prefix.String(), nil +} + +func (p *ListenPrefix) UnmarshalJSON(bytes []byte) error { + var value string + err := json.Unmarshal(bytes, &value) + if err != nil { + return err + } + prefix, err := netip.ParsePrefix(value) + if err != nil { + return err + } + *p = ListenPrefix(prefix) + return nil +} + +func (p *ListenPrefix) UnmarshalYAML(node *yaml.Node) error { + var value string + err := node.Decode(&value) + if err != nil { + return err + } + prefix, err := netip.ParsePrefix(value) + if err != nil { + return err + } + *p = ListenPrefix(prefix) + return nil +} + +func (p ListenPrefix) Build() netip.Prefix { + return netip.Prefix(p) +} + +type Tun struct { + Enable bool + Device string + Stack C.TUNStack + DNSHijack []netip.AddrPort + AutoRoute bool + AutoDetectInterface bool + RedirectToTun []string + + MTU uint32 + Inet4Address []ListenPrefix + Inet6Address []ListenPrefix + StrictRoute bool + Inet4RouteAddress []ListenPrefix + Inet6RouteAddress []ListenPrefix + IncludeUID []uint32 + IncludeUIDRange []string + ExcludeUID []uint32 + ExcludeUIDRange []string + IncludeAndroidUser []int + IncludePackage []string + ExcludePackage []string + EndpointIndependentNat bool + UDPTimeout int64 +} diff --git a/listener/sing_vmess/server.go b/listener/sing_vmess/server.go index c02a2c79..4dd046c1 100644 --- a/listener/sing_vmess/server.go +++ b/listener/sing_vmess/server.go @@ -24,7 +24,7 @@ type Listener struct { var _listener *Listener -func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (*Listener, error) { +func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) (*Listener, error) { addr, username, password, err := parseVmessURL(config) if err != nil { return nil, err diff --git a/listener/socks/tcp.go b/listener/socks/tcp.go index 8961c393..7ddc71ea 100644 --- a/listener/socks/tcp.go +++ b/listener/socks/tcp.go @@ -16,6 +16,8 @@ type Listener struct { listener net.Listener addr string closed bool + preferRulesName string + name string } // RawAddress implements C.Listener @@ -35,6 +37,10 @@ func (l *Listener) Close() error { } func New(addr string, in chan<- C.ConnContext) (*Listener, error) { + return NewWithInfos(addr,"DEFAULT-SOCKS","",in) +} + +func NewWithInfos(addr,name,preferRulesName string, in chan<- C.ConnContext) (*Listener, error) { l, err := inbound.Listen("tcp", addr) if err != nil { return nil, err @@ -43,6 +49,8 @@ func New(addr string, in chan<- C.ConnContext) (*Listener, error) { sl := &Listener{ listener: l, addr: addr, + name: name, + preferRulesName: preferRulesName, } go func() { for { @@ -53,14 +61,14 @@ func New(addr string, in chan<- C.ConnContext) (*Listener, error) { } continue } - go handleSocks(c, in) + go handleSocks(sl.name,sl.preferRulesName,c, in) } }() return sl, nil } -func handleSocks(conn net.Conn, in chan<- C.ConnContext) { +func handleSocks(name,preferRulesName string,conn net.Conn, in chan<- C.ConnContext) { conn.(*net.TCPConn).SetKeepAlive(true) bufConn := N.NewBufferedConn(conn) head, err := bufConn.Peek(1) @@ -71,24 +79,24 @@ func handleSocks(conn net.Conn, in chan<- C.ConnContext) { switch head[0] { case socks4.Version: - HandleSocks4(bufConn, in) + HandleSocks4(name,preferRulesName,bufConn, in) case socks5.Version: - HandleSocks5(bufConn, in) + HandleSocks5(name,preferRulesName,bufConn, in) default: conn.Close() } } -func HandleSocks4(conn net.Conn, in chan<- C.ConnContext) { +func HandleSocks4(name,preferRulesName string, conn net.Conn, in chan<- C.ConnContext) { addr, _, err := socks4.ServerHandshake(conn, authStore.Authenticator()) if err != nil { conn.Close() return } - in <- inbound.NewSocket(socks5.ParseAddr(addr), conn, C.SOCKS4) + in <- inbound.NewSocketWithInfos(socks5.ParseAddr(addr), conn, C.SOCKS4,name,preferRulesName) } -func HandleSocks5(conn net.Conn, in chan<- C.ConnContext) { +func HandleSocks5(name,preferRulesName string,conn net.Conn, in chan<- C.ConnContext) { target, command, err := socks5.ServerHandshake(conn, authStore.Authenticator()) if err != nil { conn.Close() @@ -99,5 +107,5 @@ func HandleSocks5(conn net.Conn, in chan<- C.ConnContext) { io.Copy(io.Discard, conn) return } - in <- inbound.NewSocket(target, conn, C.SOCKS5) + in <- inbound.NewSocketWithInfos(target, conn, C.SOCKS5,name,preferRulesName) } diff --git a/listener/socks/udp.go b/listener/socks/udp.go index 8bc439fb..6adbd257 100644 --- a/listener/socks/udp.go +++ b/listener/socks/udp.go @@ -15,6 +15,8 @@ type UDPListener struct { packetConn net.PacketConn addr string closed bool + name string + preferRulesName string } // RawAddress implements C.Listener @@ -33,7 +35,11 @@ func (l *UDPListener) Close() error { return l.packetConn.Close() } -func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (*UDPListener, error) { +func NewUDP(addr string, in chan<- *C.PacketAdapter) (*UDPListener, error) { + return NewUDPWithInfos(addr,"DEFAULT-SOCKS","",in) +} + +func NewUDPWithInfos(addr,name ,preferRulesName string, in chan<- *C.PacketAdapter) (*UDPListener, error) { l, err := net.ListenPacket("udp", addr) if err != nil { return nil, err @@ -46,6 +52,8 @@ func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (*UDPListener, error) sl := &UDPListener{ packetConn: l, addr: addr, + preferRulesName: preferRulesName, + name: name, } go func() { for { @@ -58,14 +66,14 @@ func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (*UDPListener, error) } continue } - handleSocksUDP(l, in, buf[:n], remoteAddr) + handleSocksUDP(sl.name,sl.preferRulesName,l, in, buf[:n], remoteAddr) } }() return sl, nil } -func handleSocksUDP(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buf []byte, addr net.Addr) { +func handleSocksUDP(name,preferRulesName string,pc net.PacketConn, in chan<- *C.PacketAdapter, buf []byte, addr net.Addr) { target, payload, err := socks5.DecodeUDPPacket(buf) if err != nil { // Unresolved UDP packet, return buffer to the pool @@ -79,7 +87,7 @@ func handleSocksUDP(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buf []b bufRef: buf, } select { - case in <- inbound.NewPacket(target, packet, C.SOCKS5): + case in <- inbound.NewPacketWithInfos(target, packet, C.SOCKS5,name,preferRulesName): default: } } diff --git a/listener/tproxy/tproxy.go b/listener/tproxy/tproxy.go index 1a09f366..c546008d 100644 --- a/listener/tproxy/tproxy.go +++ b/listener/tproxy/tproxy.go @@ -12,6 +12,8 @@ type Listener struct { listener net.Listener addr string closed bool + name string + preferRulesName string } // RawAddress implements C.Listener @@ -30,13 +32,17 @@ func (l *Listener) Close() error { return l.listener.Close() } -func (l *Listener) handleTProxy(conn net.Conn, in chan<- C.ConnContext) { +func (l *Listener) handleTProxy(name,preferRulesName string ,conn net.Conn, in chan<- C.ConnContext) { target := socks5.ParseAddrToSocksAddr(conn.LocalAddr()) conn.(*net.TCPConn).SetKeepAlive(true) - in <- inbound.NewSocket(target, conn, C.TPROXY) + in <- inbound.NewSocketWithInfos(target, conn, C.TPROXY,name,preferRulesName) } func New(addr string, in chan<- C.ConnContext) (*Listener, error) { + return NewWithInfos(addr,"DEFAULT-TPROXY","",in) +} + +func NewWithInfos(addr,name,preferRulesName string, in chan<- C.ConnContext) (*Listener, error) { l, err := net.Listen("tcp", addr) if err != nil { return nil, err @@ -56,6 +62,8 @@ func New(addr string, in chan<- C.ConnContext) (*Listener, error) { rl := &Listener{ listener: l, addr: addr, + name: name, + preferRulesName: preferRulesName, } go func() { @@ -67,7 +75,7 @@ func New(addr string, in chan<- C.ConnContext) (*Listener, error) { } continue } - go rl.handleTProxy(c, in) + go rl.handleTProxy(rl.name,rl.preferRulesName,c, in) } }() diff --git a/listener/tproxy/udp.go b/listener/tproxy/udp.go index 90d0a97d..f34e5525 100644 --- a/listener/tproxy/udp.go +++ b/listener/tproxy/udp.go @@ -11,9 +11,11 @@ import ( ) type UDPListener struct { - packetConn net.PacketConn - addr string - closed bool + packetConn net.PacketConn + addr string + closed bool + name string + preferRulesName string } // RawAddress implements C.Listener @@ -32,7 +34,11 @@ func (l *UDPListener) Close() error { return l.packetConn.Close() } -func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (*UDPListener, error) { +func NewUDP(addr string, in chan<- *C.PacketAdapter) (*UDPListener, error) { + return NewUDPWithInfos(addr, "DEFAULT-TPROXY", "", in) +} + +func NewUDPWithInfos(addr, name, preferRulesName string, in chan<- *C.PacketAdapter) (*UDPListener, error) { l, err := net.ListenPacket("udp", addr) if err != nil { return nil, err @@ -77,14 +83,14 @@ func NewUDP(addr string, in chan<- *inbound.PacketAdapter) (*UDPListener, error) // try to unmap 4in6 address lAddr = netip.AddrPortFrom(lAddr.Addr().Unmap(), lAddr.Port()) } - handlePacketConn(l, in, buf[:n], lAddr, rAddr) + handlePacketConn(rl.name, rl.preferRulesName, l, in, buf[:n], lAddr, rAddr) } }() return rl, nil } -func handlePacketConn(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buf []byte, lAddr, rAddr netip.AddrPort) { +func handlePacketConn(name, preferRulesName string, pc net.PacketConn, in chan<- *C.PacketAdapter, buf []byte, lAddr, rAddr netip.AddrPort) { target := socks5.AddrFromStdAddrPort(rAddr) pkt := &packet{ pc: pc, @@ -92,7 +98,7 @@ func handlePacketConn(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buf [ buf: buf, } select { - case in <- inbound.NewPacket(target, pkt, C.TPROXY): + case in <- inbound.NewPacketWithInfos(target, pkt, C.TPROXY, name, preferRulesName): default: } } diff --git a/listener/tuic/server.go b/listener/tuic/server.go index 64ec8489..565f258e 100644 --- a/listener/tuic/server.go +++ b/listener/tuic/server.go @@ -2,29 +2,47 @@ package tuic import ( "crypto/tls" + "encoding/json" "net" "strings" "time" "github.com/metacubex/quic-go" - "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/common/sockopt" - "github.com/Dreamacro/clash/config" C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/transport/socks5" "github.com/Dreamacro/clash/transport/tuic" ) +type TuicServer struct { + Enable bool + Listen string + Token []string + Certificate string + PrivateKey string + CongestionController string + MaxIdleTime int + AuthenticationTimeout int + ALPN []string + MaxUdpRelayPacketSize int +} + +func (t TuicServer) String() string { + b, _ := json.Marshal(t) + return string(b) +} + type Listener struct { closed bool - config config.TuicServer + config TuicServer udpListeners []net.PacketConn servers []*tuic.Server } -func New(config config.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (*Listener, error) { +func New(config TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) (*Listener, error) { cert, err := tls.LoadX509KeyPair(config.Certificate, config.PrivateKey) if err != nil { return nil, err @@ -122,6 +140,6 @@ func (l *Listener) Close() { } } -func (l *Listener) Config() config.TuicServer { +func (l *Listener) Config() TuicServer { return l.config } diff --git a/listener/tunnel/tunnel.go b/listener/tunnel/tunnel.go new file mode 100644 index 00000000..c3fc759c --- /dev/null +++ b/listener/tunnel/tunnel.go @@ -0,0 +1,69 @@ +package tunnel + +import ( + "fmt" + "net" + "strings" + + "github.com/samber/lo" +) + +type tunnel struct { + Network []string `yaml:"network"` + Address string `yaml:"address"` + Target string `yaml:"target"` + Proxy string `yaml:"proxy"` +} + +type Tunnel tunnel + +// UnmarshalYAML implements yaml.Unmarshaler +func (t *Tunnel) UnmarshalYAML(unmarshal func(any) error) error { + var tp string + if err := unmarshal(&tp); err != nil { + var inner tunnel + if err := unmarshal(&inner); err != nil { + return err + } + + *t = Tunnel(inner) + return nil + } + + // parse udp/tcp,address,target,proxy + parts := lo.Map(strings.Split(tp, ","), func(s string, _ int) string { + return strings.TrimSpace(s) + }) + if len(parts) != 3 && len(parts) != 4 { + return fmt.Errorf("invalid tunnel config %s", tp) + } + network := strings.Split(parts[0], "/") + + // validate network + for _, n := range network { + switch n { + case "tcp", "udp": + default: + return fmt.Errorf("invalid tunnel network %s", n) + } + } + + // validate address and target + address := parts[1] + target := parts[2] + for _, addr := range []string{address, target} { + if _, _, err := net.SplitHostPort(addr); err != nil { + return fmt.Errorf("invalid tunnel target or address %s", addr) + } + } + + *t = Tunnel(tunnel{ + Network: network, + Address: address, + Target: target, + }) + if len(parts) == 4 { + t.Proxy = parts[3] + } + return nil +} diff --git a/listener/tunnel/udp.go b/listener/tunnel/udp.go index ee0ecbaf..2f8ddeb6 100644 --- a/listener/tunnel/udp.go +++ b/listener/tunnel/udp.go @@ -34,7 +34,7 @@ func (l *PacketConn) Close() error { return l.conn.Close() } -func NewUDP(addr, target, proxy string, in chan<- *inbound.PacketAdapter) (*PacketConn, error) { +func NewUDP(addr, target, proxy string, in chan<- *C.PacketAdapter) (*PacketConn, error) { l, err := net.ListenPacket("udp", addr) if err != nil { return nil, err @@ -69,7 +69,7 @@ func NewUDP(addr, target, proxy string, in chan<- *inbound.PacketAdapter) (*Pack return sl, nil } -func (l *PacketConn) handleUDP(pc net.PacketConn, in chan<- *inbound.PacketAdapter, buf []byte, addr net.Addr) { +func (l *PacketConn) handleUDP(pc net.PacketConn, in chan<- *C.PacketAdapter, buf []byte, addr net.Addr) { packet := &packet{ pc: pc, rAddr: addr, diff --git a/rules/logic/and.go b/rules/logic/and.go index a8fc1bad..ffd7c262 100644 --- a/rules/logic/and.go +++ b/rules/logic/and.go @@ -20,7 +20,7 @@ func (A *AND) ShouldFindProcess() bool { } func NewAND(payload string, adapter string, - parse func(tp, payload, target string, params []string, subRules *map[string][]C.Rule) (parsed C.Rule, parseErr error)) (*AND, error) { + parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) (*AND, error) { and := &AND{Base: &common.Base{}, payload: payload, adapter: adapter} rules, err := ParseRuleByPayload(payload, parse) if err != nil { diff --git a/rules/logic/common.go b/rules/logic/common.go index 080771ba..1385463b 100644 --- a/rules/logic/common.go +++ b/rules/logic/common.go @@ -9,7 +9,7 @@ import ( _ "unsafe" ) -func ParseRuleByPayload(payload string, parseRule func(tp, payload, target string, params []string, subRules *map[string][]C.Rule) (parsed C.Rule, parseErr error)) ([]C.Rule, error) { +func ParseRuleByPayload(payload string, parseRule func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) ([]C.Rule, error) { regex, err := regexp.Compile("\\(.*\\)") if err != nil { return nil, err @@ -59,7 +59,7 @@ func payloadToRule(subPayload string, parseRule func(tp, payload, target string, return parseRule(tp, param[0], "", param[1:]) } -func parseLogicSubRule(parseRule func(tp, payload, target string, params []string, subRules *map[string][]C.Rule) (parsed C.Rule, parseErr error)) func(tp, payload, target string, params []string) (parsed C.Rule, parseErr error) { +func parseLogicSubRule(parseRule func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) func(tp, payload, target string, params []string) (parsed C.Rule, parseErr error) { return func(tp, payload, target string, params []string) (parsed C.Rule, parseErr error) { switch tp { case "MATCH", "SUB-RULE": diff --git a/rules/logic/not.go b/rules/logic/not.go index 6a5b34d8..c109a2c9 100644 --- a/rules/logic/not.go +++ b/rules/logic/not.go @@ -17,7 +17,7 @@ func (not *NOT) ShouldFindProcess() bool { return false } -func NewNOT(payload string, adapter string, parse func(tp, payload, target string, params []string, subRules *map[string][]C.Rule) (parsed C.Rule, parseErr error)) (*NOT, error) { +func NewNOT(payload string, adapter string, parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) (*NOT, error) { not := &NOT{Base: &common.Base{}, adapter: adapter} rule, err := ParseRuleByPayload(payload, parse) if err != nil { diff --git a/rules/logic/or.go b/rules/logic/or.go index d1aae9ac..f63bae4b 100644 --- a/rules/logic/or.go +++ b/rules/logic/or.go @@ -45,7 +45,7 @@ func (or *OR) ShouldResolveIP() bool { return or.needIP } -func NewOR(payload string, adapter string, parse func(tp, payload, target string, params []string, subRules *map[string][]C.Rule) (parsed C.Rule, parseErr error)) (*OR, error) { +func NewOR(payload string, adapter string, parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) (*OR, error) { or := &OR{Base: &common.Base{}, payload: payload, adapter: adapter} rules, err := ParseRuleByPayload(payload, parse) if err != nil { diff --git a/rules/logic/sub_rules.go b/rules/logic/sub_rules.go index b4ad7613..75852979 100644 --- a/rules/logic/sub_rules.go +++ b/rules/logic/sub_rules.go @@ -12,13 +12,13 @@ type SubRule struct { payload string payloadRule C.Rule subName string - subRules *map[string][]C.Rule + subRules map[string][]C.Rule shouldFindProcess *bool shouldResolveIP *bool } -func NewSubRule(payload, subName string, sub *map[string][]C.Rule, - parse func(tp, payload, target string, params []string, subRules *map[string][]C.Rule) (parsed C.Rule, parseErr error)) (*SubRule, error) { +func NewSubRule(payload, subName string, sub map[string][]C.Rule, + parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) (*SubRule, error) { payloadRule, err := ParseRuleByPayload(fmt.Sprintf("(%s)", payload), parse) if err != nil { return nil, err @@ -45,8 +45,8 @@ func (r *SubRule) Match(metadata *C.Metadata) (bool, string) { return match(metadata, r.subName, r.subRules) } -func match(metadata *C.Metadata, name string, subRules *map[string][]C.Rule) (bool, string) { - for _, rule := range (*subRules)[name] { +func match(metadata *C.Metadata, name string, subRules map[string][]C.Rule) (bool, string) { + for _, rule := range subRules[name] { if m, a := rule.Match(metadata); m { if rule.RuleType() == C.SubRules { match(metadata, rule.Adapter(), subRules) @@ -61,7 +61,7 @@ func match(metadata *C.Metadata, name string, subRules *map[string][]C.Rule) (bo func (r *SubRule) ShouldResolveIP() bool { if r.shouldResolveIP == nil { s := false - for _, rule := range (*r.subRules)[r.subName] { + for _, rule := range r.subRules[r.subName] { s = s || rule.ShouldResolveIP() } r.shouldResolveIP = &s @@ -73,7 +73,7 @@ func (r *SubRule) ShouldResolveIP() bool { func (r *SubRule) ShouldFindProcess() bool { if r.shouldFindProcess == nil { s := false - for _, rule := range (*r.subRules)[r.subName] { + for _, rule := range r.subRules[r.subName] { s = s || rule.ShouldFindProcess() } r.shouldFindProcess = &s diff --git a/rules/parser.go b/rules/parser.go index 4e1d9044..1a336225 100644 --- a/rules/parser.go +++ b/rules/parser.go @@ -8,7 +8,7 @@ import ( RP "github.com/Dreamacro/clash/rules/provider" ) -func ParseRule(tp, payload, target string, params []string, subRules *map[string][]C.Rule) (parsed C.Rule, parseErr error) { +func ParseRule(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error) { switch tp { case "DOMAIN": parsed = RC.NewDomain(payload, target) diff --git a/rules/provider/classical_strategy.go b/rules/provider/classical_strategy.go index 727688fc..12623271 100644 --- a/rules/provider/classical_strategy.go +++ b/rules/provider/classical_strategy.go @@ -66,7 +66,7 @@ func ruleParse(ruleRaw string) (string, string, []string) { return "", "", nil } -func NewClassicalStrategy(parse func(tp, payload, target string, params []string, subRules *map[string][]C.Rule) (parsed C.Rule, parseErr error)) *classicalStrategy { +func NewClassicalStrategy(parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) *classicalStrategy { return &classicalStrategy{rules: []C.Rule{}, parse: func(tp, payload, target string, params []string) (parsed C.Rule, parseErr error) { switch tp { case "MATCH", "SUB-RULE": diff --git a/rules/provider/parse.go b/rules/provider/parse.go index 86e21a30..206bef10 100644 --- a/rules/provider/parse.go +++ b/rules/provider/parse.go @@ -17,7 +17,7 @@ type ruleProviderSchema struct { Interval int `provider:"interval,omitempty"` } -func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(tp, payload, target string, params []string, subRules *map[string][]C.Rule) (parsed C.Rule, parseErr error)) (P.RuleProvider, error) { +func ParseRuleProvider(name string, mapping map[string]interface{}, parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) (P.RuleProvider, error) { schema := &ruleProviderSchema{} decoder := structure.NewDecoder(structure.Option{TagName: "provider", WeaklyTypedInput: true}) if err := decoder.Decode(mapping, schema); err != nil { diff --git a/rules/provider/provider.go b/rules/provider/provider.go index 9ae125fb..347bebaa 100644 --- a/rules/provider/provider.go +++ b/rules/provider/provider.go @@ -103,7 +103,7 @@ func (rp *ruleSetProvider) MarshalJSON() ([]byte, error) { } func NewRuleSetProvider(name string, behavior P.RuleType, interval time.Duration, vehicle P.Vehicle, - parse func(tp, payload, target string, params []string, subRules *map[string][]C.Rule) (parsed C.Rule, parseErr error)) P.RuleProvider { + parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) P.RuleProvider { rp := &ruleSetProvider{ behavior: behavior, } @@ -126,7 +126,7 @@ func NewRuleSetProvider(name string, behavior P.RuleType, interval time.Duration return wrapper } -func newStrategy(behavior P.RuleType, parse func(tp, payload, target string, params []string, subRules *map[string][]C.Rule) (parsed C.Rule, parseErr error)) ruleStrategy { +func newStrategy(behavior P.RuleType, parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) ruleStrategy { switch behavior { case P.Domain: strategy := NewDomainStrategy() diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 62a1ef5a..6a0af92e 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -13,7 +13,6 @@ import ( "github.com/jpillora/backoff" - "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/component/nat" P "github.com/Dreamacro/clash/component/process" "github.com/Dreamacro/clash/component/resolver" @@ -27,9 +26,10 @@ import ( var ( tcpQueue = make(chan C.ConnContext, 200) - udpQueue = make(chan *inbound.PacketAdapter, 200) + udpQueue = make(chan *C.PacketAdapter, 200) natTable = nat.New() rules []C.Rule + subRules map[string][]C.Rule proxies = make(map[string]C.Proxy) providers map[string]provider.ProxyProvider ruleProviders map[string]provider.RuleProvider @@ -77,7 +77,7 @@ func TCPIn() chan<- C.ConnContext { } // UDPIn return fan-in udp queue -func UDPIn() chan<- *inbound.PacketAdapter { +func UDPIn() chan<- *C.PacketAdapter { return udpQueue } @@ -87,10 +87,11 @@ func Rules() []C.Rule { } // UpdateRules handle update rules -func UpdateRules(newRules []C.Rule, rp map[string]provider.RuleProvider) { +func UpdateRules(newRules []C.Rule, newSubRule map[string][]C.Rule, rp map[string]provider.RuleProvider) { configMux.Lock() rules = newRules ruleProviders = rp + subRules = newSubRule configMux.Unlock() } @@ -216,7 +217,7 @@ func resolveMetadata(ctx C.PlainContext, metadata *C.Metadata) (proxy C.Proxy, r return } -func handleUDPConn(packet *inbound.PacketAdapter) { +func handleUDPConn(packet *C.PacketAdapter) { metadata := packet.Metadata() if !metadata.Valid() { log.Warnln("[Metadata] not valid: %#v", metadata) @@ -435,7 +436,7 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { resolved = true } - for _, rule := range rules { + for _, rule := range getRules(metadata) { if !resolved && shouldResolveIP(rule, metadata) { func() { ctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout) @@ -495,6 +496,14 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { return proxies["DIRECT"], nil, nil } +func getRules(metadata *C.Metadata) []C.Rule { + if sr, ok := subRules[metadata.PreferRulesName]; ok { + return sr + } else { + return rules + } +} + func retry[T any](ctx context.Context, ft func(context.Context) (T, error), fe func(err error)) (t T, err error) { b := &backoff.Backoff{ Min: 10 * time.Millisecond, From 6fc62da7ae5893b5a0ae573a5fda223735920732 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 4 Dec 2022 14:37:52 +0800 Subject: [PATCH 192/250] chore: change C.PacketAdapter from a struct to an interface --- adapter/inbound/packet.go | 17 +++- config/config.go | 125 +++++++++++++-------------- constant/adapters.go | 6 ++ constant/listener.go | 20 +---- hub/executor/executor.go | 9 +- hub/route/configs.go | 33 ++++--- hub/route/server.go | 2 +- listener/config/tuic.go | 23 +++++ listener/{sing_tun => config}/tun.go | 2 +- listener/http/server.go | 24 ++--- listener/inbound/base.go | 2 +- listener/inbound/http.go | 2 +- listener/inbound/mixed.go | 2 +- listener/inbound/redir.go | 2 +- listener/inbound/socks.go | 2 +- listener/inbound/tproxy.go | 2 +- listener/listener.go | 37 ++++---- listener/parse.go | 25 ++++-- listener/redir/tcp.go | 24 ++--- listener/shadowsocks/tcp.go | 2 +- listener/shadowsocks/udp.go | 4 +- listener/sing/sing.go | 2 +- listener/sing_shadowsocks/server.go | 2 +- listener/sing_tun/server.go | 15 ++-- listener/sing_vmess/server.go | 2 +- listener/socks/tcp.go | 36 ++++---- listener/socks/udp.go | 26 +++--- listener/tproxy/tproxy.go | 24 ++--- listener/tproxy/udp.go | 6 +- listener/tuic/server.go | 28 ++---- listener/tunnel/udp.go | 4 +- tunnel/tunnel.go | 8 +- 32 files changed, 267 insertions(+), 251 deletions(-) create mode 100644 listener/config/tuic.go rename listener/{sing_tun => config}/tun.go (99%) diff --git a/adapter/inbound/packet.go b/adapter/inbound/packet.go index 1b377d36..524c8f52 100644 --- a/adapter/inbound/packet.go +++ b/adapter/inbound/packet.go @@ -5,9 +5,18 @@ import ( "github.com/Dreamacro/clash/transport/socks5" ) +// PacketAdapter is a UDP Packet adapter for socks/redir/tun +type PacketAdapter struct { + C.UDPPacket + metadata *C.Metadata +} +// Metadata returns destination metadata +func (s *PacketAdapter) Metadata() *C.Metadata { + return s.metadata +} -func NewPacketWithInfos(target socks5.Addr, packet C.UDPPacket, source C.Type, inName , preferRulesName string) *C.PacketAdapter { +func NewPacketWithInfos(target socks5.Addr, packet C.UDPPacket, source C.Type, inName, preferRulesName string) C.PacketAdapter { metadata := parseSocksAddr(target) metadata.NetWork = C.UDP metadata.Type = source @@ -24,13 +33,13 @@ func NewPacketWithInfos(target socks5.Addr, packet C.UDPPacket, source C.Type, i } } - return C.NewPacketAdapter( + return &PacketAdapter{ packet, metadata, - ) + } } // NewPacket is PacketAdapter generator -func NewPacket(target socks5.Addr, packet C.UDPPacket, source C.Type) *C.PacketAdapter { +func NewPacket(target socks5.Addr, packet C.UDPPacket, source C.Type) C.PacketAdapter { return NewPacketWithInfos(target, packet, source, "", "") } diff --git a/config/config.go b/config/config.go index 5ac357b2..c44ac250 100644 --- a/config/config.go +++ b/config/config.go @@ -14,16 +14,11 @@ import ( "strings" "time" - "github.com/Dreamacro/clash/common/utils" - "github.com/Dreamacro/clash/listener/sing_tun" - "github.com/Dreamacro/clash/listener/tunnel" - R "github.com/Dreamacro/clash/rules" - RP "github.com/Dreamacro/clash/rules/provider" - L "github.com/Dreamacro/clash/listener" "github.com/Dreamacro/clash/adapter" "github.com/Dreamacro/clash/adapter/outbound" "github.com/Dreamacro/clash/adapter/outboundgroup" "github.com/Dreamacro/clash/adapter/provider" + "github.com/Dreamacro/clash/common/utils" "github.com/Dreamacro/clash/component/auth" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/fakeip" @@ -34,7 +29,12 @@ import ( providerTypes "github.com/Dreamacro/clash/constant/provider" snifferTypes "github.com/Dreamacro/clash/constant/sniffer" "github.com/Dreamacro/clash/dns" + L "github.com/Dreamacro/clash/listener" + LC "github.com/Dreamacro/clash/listener/config" + "github.com/Dreamacro/clash/listener/tunnel" "github.com/Dreamacro/clash/log" + R "github.com/Dreamacro/clash/rules" + RP "github.com/Dreamacro/clash/rules/provider" T "github.com/Dreamacro/clash/tunnel" "gopkg.in/yaml.v3" @@ -148,21 +148,21 @@ type Tun struct { AutoDetectInterface bool `yaml:"auto-detect-interface" json:"auto-detect-interface"` RedirectToTun []string `yaml:"-" json:"-"` - MTU uint32 `yaml:"mtu" json:"mtu,omitempty"` - Inet4Address []sing_tun.ListenPrefix `yaml:"inet4-address" json:"inet4-address,omitempty"` - Inet6Address []sing_tun.ListenPrefix `yaml:"inet6-address" json:"inet6-address,omitempty"` - StrictRoute bool `yaml:"strict-route" json:"strict-route,omitempty"` - Inet4RouteAddress []sing_tun.ListenPrefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"` - Inet6RouteAddress []sing_tun.ListenPrefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"` - IncludeUID []uint32 `yaml:"include-uid" json:"include-uid,omitempty"` - IncludeUIDRange []string `yaml:"include-uid-range" json:"include-uid-range,omitempty"` - ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"` - ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"` - IncludeAndroidUser []int `yaml:"include-android-user" json:"include-android-user,omitempty"` - IncludePackage []string `yaml:"include-package" json:"include-package,omitempty"` - ExcludePackage []string `yaml:"exclude-package" json:"exclude-package,omitempty"` - EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"` - UDPTimeout int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"` + MTU uint32 `yaml:"mtu" json:"mtu,omitempty"` + Inet4Address []LC.ListenPrefix `yaml:"inet4-address" json:"inet4-address,omitempty"` + Inet6Address []LC.ListenPrefix `yaml:"inet6-address" json:"inet6-address,omitempty"` + StrictRoute bool `yaml:"strict-route" json:"strict-route,omitempty"` + Inet4RouteAddress []LC.ListenPrefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"` + Inet6RouteAddress []LC.ListenPrefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"` + IncludeUID []uint32 `yaml:"include-uid" json:"include-uid,omitempty"` + IncludeUIDRange []string `yaml:"include-uid-range" json:"include-uid-range,omitempty"` + ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"` + ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"` + IncludeAndroidUser []int `yaml:"include-android-user" json:"include-android-user,omitempty"` + IncludePackage []string `yaml:"include-package" json:"include-package,omitempty"` + ExcludePackage []string `yaml:"exclude-package" json:"exclude-package,omitempty"` + EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"` + UDPTimeout int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"` } // IPTables config @@ -244,19 +244,19 @@ type RawTun struct { MTU uint32 `yaml:"mtu" json:"mtu,omitempty"` //Inet4Address []ListenPrefix `yaml:"inet4-address" json:"inet4_address,omitempty"` - Inet6Address []sing_tun.ListenPrefix `yaml:"inet6-address" json:"inet6_address,omitempty"` - StrictRoute bool `yaml:"strict-route" json:"strict_route,omitempty"` - Inet4RouteAddress []sing_tun.ListenPrefix `yaml:"inet4_route_address" json:"inet4_route_address,omitempty"` - Inet6RouteAddress []sing_tun.ListenPrefix `yaml:"inet6_route_address" json:"inet6_route_address,omitempty"` - IncludeUID []uint32 `yaml:"include-uid" json:"include_uid,omitempty"` - IncludeUIDRange []string `yaml:"include-uid-range" json:"include_uid_range,omitempty"` - ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude_uid,omitempty"` - ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude_uid_range,omitempty"` - IncludeAndroidUser []int `yaml:"include-android-user" json:"include_android_user,omitempty"` - IncludePackage []string `yaml:"include-package" json:"include_package,omitempty"` - ExcludePackage []string `yaml:"exclude-package" json:"exclude_package,omitempty"` - EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint_independent_nat,omitempty"` - UDPTimeout int64 `yaml:"udp-timeout" json:"udp_timeout,omitempty"` + Inet6Address []LC.ListenPrefix `yaml:"inet6-address" json:"inet6_address,omitempty"` + StrictRoute bool `yaml:"strict-route" json:"strict_route,omitempty"` + Inet4RouteAddress []LC.ListenPrefix `yaml:"inet4_route_address" json:"inet4_route_address,omitempty"` + Inet6RouteAddress []LC.ListenPrefix `yaml:"inet6_route_address" json:"inet6_route_address,omitempty"` + IncludeUID []uint32 `yaml:"include-uid" json:"include_uid,omitempty"` + IncludeUIDRange []string `yaml:"include-uid-range" json:"include_uid_range,omitempty"` + ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude_uid,omitempty"` + ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude_uid_range,omitempty"` + IncludeAndroidUser []int `yaml:"include-android-user" json:"include_android_user,omitempty"` + IncludePackage []string `yaml:"include-package" json:"include_package,omitempty"` + ExcludePackage []string `yaml:"exclude-package" json:"exclude_package,omitempty"` + EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint_independent_nat,omitempty"` + UDPTimeout int64 `yaml:"udp-timeout" json:"udp_timeout,omitempty"` } type RawTuicServer struct { @@ -272,34 +272,33 @@ type RawTuicServer struct { MaxUdpRelayPacketSize int `yaml:"max-udp-relay-packet-size" json:"max-udp-relay-packet-size,omitempty"` } - 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"` - ShadowSocksConfig string `yaml:"ss-config"` - VmessConfig string `yaml:"vmess-config"` - InboundTfo bool `yaml:"inbound-tfo"` - Authentication []string `yaml:"authentication"` - 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"` - ExternalController string `yaml:"external-controller"` - ExternalControllerTLS string `yaml:"external-controller-tls"` - ExternalUI string `yaml:"external-ui"` - Secret string `yaml:"secret"` - Interface string `yaml:"interface-name"` - RoutingMark int `yaml:"routing-mark"` - Tunnels []tunnel.Tunnel `yaml:"tunnels"` - GeodataMode bool `yaml:"geodata-mode"` - GeodataLoader string `yaml:"geodata-loader"` - TCPConcurrent bool `yaml:"tcp-concurrent" json:"tcp-concurrent"` - EnableProcess bool `yaml:"enable-process" json:"enable-process"` + Port int `yaml:"port"` + SocksPort int `yaml:"socks-port"` + RedirPort int `yaml:"redir-port"` + TProxyPort int `yaml:"tproxy-port"` + MixedPort int `yaml:"mixed-port"` + ShadowSocksConfig string `yaml:"ss-config"` + VmessConfig string `yaml:"vmess-config"` + InboundTfo bool `yaml:"inbound-tfo"` + Authentication []string `yaml:"authentication"` + 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"` + ExternalController string `yaml:"external-controller"` + ExternalControllerTLS string `yaml:"external-controller-tls"` + ExternalUI string `yaml:"external-ui"` + Secret string `yaml:"secret"` + Interface string `yaml:"interface-name"` + RoutingMark int `yaml:"routing-mark"` + Tunnels []tunnel.Tunnel `yaml:"tunnels"` + GeodataMode bool `yaml:"geodata-mode"` + GeodataLoader string `yaml:"geodata-loader"` + TCPConcurrent bool `yaml:"tcp-concurrent" json:"tcp-concurrent"` + EnableProcess bool `yaml:"enable-process" json:"enable-process"` Sniffer RawSniffer `yaml:"sniffer"` ProxyProvider map[string]map[string]any `yaml:"proxy-providers"` @@ -384,7 +383,7 @@ func UnmarshalRawConfig(buf []byte) (*RawConfig, error) { DNSHijack: []string{"0.0.0.0:53"}, // default hijack all dns query AutoRoute: true, AutoDetectInterface: true, - Inet6Address: []sing_tun.ListenPrefix{sing_tun.ListenPrefix(netip.MustParsePrefix("fdfe:dcba:9876::1/126"))}, + Inet6Address: []LC.ListenPrefix{LC.ListenPrefix(netip.MustParsePrefix("fdfe:dcba:9876::1/126"))}, }, TuicServer: RawTuicServer{ Enable: false, @@ -1232,7 +1231,7 @@ func parseTun(rawTun RawTun, general *General) error { RedirectToTun: rawTun.RedirectToTun, MTU: rawTun.MTU, - Inet4Address: []sing_tun.ListenPrefix{sing_tun.ListenPrefix(tunAddressPrefix)}, + Inet4Address: []LC.ListenPrefix{LC.ListenPrefix(tunAddressPrefix)}, Inet6Address: rawTun.Inet6Address, StrictRoute: rawTun.StrictRoute, Inet4RouteAddress: rawTun.Inet4RouteAddress, diff --git a/constant/adapters.go b/constant/adapters.go index d25023e2..f4b8ba4d 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -210,3 +210,9 @@ type UDPPacket interface { type UDPPacketInAddr interface { InAddr() net.Addr } + +// PacketAdapter is a UDP Packet adapter for socks/redir/tun +type PacketAdapter interface { + UDPPacket + Metadata() *Metadata +} diff --git a/constant/listener.go b/constant/listener.go index 370bf892..4edae67c 100644 --- a/constant/listener.go +++ b/constant/listener.go @@ -16,26 +16,8 @@ type AdvanceListener interface { type NewListener interface { Name() string - ReCreate(tcpIn chan<- ConnContext,udpIn chan<-*PacketAdapter) error + ReCreate(tcpIn chan<- ConnContext, udpIn chan<- PacketAdapter) error Close() error Address() string RawAddress() string } - -// PacketAdapter is a UDP Packet adapter for socks/redir/tun -type PacketAdapter struct { - UDPPacket - metadata *Metadata -} - -func NewPacketAdapter(udppacket UDPPacket,metadata *Metadata)*PacketAdapter{ -return &PacketAdapter{ - udppacket, - metadata, -} -} - -// Metadata returns destination metadata -func (s *PacketAdapter) Metadata() *Metadata { - return s.metadata -} \ No newline at end of file diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 4a18b3c4..e1ac7f6e 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -8,6 +8,7 @@ import ( "sync" "github.com/Dreamacro/clash/adapter" + "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/adapter/outboundgroup" "github.com/Dreamacro/clash/component/auth" "github.com/Dreamacro/clash/component/dialer" @@ -25,11 +26,9 @@ import ( "github.com/Dreamacro/clash/dns" "github.com/Dreamacro/clash/listener" authStore "github.com/Dreamacro/clash/listener/auth" - "github.com/Dreamacro/clash/adapter/inbound" + LC "github.com/Dreamacro/clash/listener/config" "github.com/Dreamacro/clash/listener/inner" - "github.com/Dreamacro/clash/listener/sing_tun" "github.com/Dreamacro/clash/listener/tproxy" - "github.com/Dreamacro/clash/listener/tuic" T "github.com/Dreamacro/clash/listener/tunnel" "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/tunnel" @@ -281,7 +280,7 @@ func updateTun(general *config.General) { if general == nil { return } - listener.ReCreateTun(sing_tun.Tun(general.Tun), tunnel.TCPIn(), tunnel.UDPIn()) + listener.ReCreateTun(LC.Tun(general.Tun), tunnel.TCPIn(), tunnel.UDPIn()) listener.ReCreateRedirToTun(general.Tun.RedirectToTun) } @@ -367,7 +366,7 @@ func updateGeneral(general *config.General, force bool) { listener.ReCreateMixed(general.MixedPort, tcpIn, udpIn) listener.ReCreateShadowSocks(general.ShadowSocksConfig, tcpIn, udpIn) listener.ReCreateVmess(general.VmessConfig, tcpIn, udpIn) - listener.ReCreateTuic(tuic.TuicServer(general.TuicServer), tcpIn, udpIn) + listener.ReCreateTuic(LC.TuicServer(general.TuicServer), tcpIn, udpIn) } func updateUsers(users []auth.AuthUser) { diff --git a/hub/route/configs.go b/hub/route/configs.go index 4b3ca328..3699b9d3 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -13,8 +13,7 @@ import ( C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/hub/executor" P "github.com/Dreamacro/clash/listener" - "github.com/Dreamacro/clash/listener/sing_tun" - "github.com/Dreamacro/clash/listener/tuic" + LC "github.com/Dreamacro/clash/listener/config" "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/tunnel" @@ -69,19 +68,19 @@ type tunSchema struct { MTU *uint32 `yaml:"mtu" json:"mtu,omitempty"` //Inet4Address *[]config.ListenPrefix `yaml:"inet4-address" json:"inet4-address,omitempty"` - Inet6Address *[]sing_tun.ListenPrefix `yaml:"inet6-address" json:"inet6-address,omitempty"` - StrictRoute *bool `yaml:"strict-route" json:"strict-route,omitempty"` - Inet4RouteAddress *[]sing_tun.ListenPrefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"` - Inet6RouteAddress *[]sing_tun.ListenPrefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"` - IncludeUID *[]uint32 `yaml:"include-uid" json:"include-uid,omitempty"` - IncludeUIDRange *[]string `yaml:"include-uid-range" json:"include-uid-range,omitempty"` - ExcludeUID *[]uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"` - ExcludeUIDRange *[]string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"` - IncludeAndroidUser *[]int `yaml:"include-android-user" json:"include-android-user,omitempty"` - IncludePackage *[]string `yaml:"include-package" json:"include-package,omitempty"` - ExcludePackage *[]string `yaml:"exclude-package" json:"exclude-package,omitempty"` - EndpointIndependentNat *bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"` - UDPTimeout *int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"` + Inet6Address *[]LC.ListenPrefix `yaml:"inet6-address" json:"inet6-address,omitempty"` + StrictRoute *bool `yaml:"strict-route" json:"strict-route,omitempty"` + Inet4RouteAddress *[]LC.ListenPrefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"` + Inet6RouteAddress *[]LC.ListenPrefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"` + IncludeUID *[]uint32 `yaml:"include-uid" json:"include-uid,omitempty"` + IncludeUIDRange *[]string `yaml:"include-uid-range" json:"include-uid-range,omitempty"` + ExcludeUID *[]uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"` + ExcludeUIDRange *[]string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"` + IncludeAndroidUser *[]int `yaml:"include-android-user" json:"include-android-user,omitempty"` + IncludePackage *[]string `yaml:"include-package" json:"include-package,omitempty"` + ExcludePackage *[]string `yaml:"exclude-package" json:"exclude-package,omitempty"` + EndpointIndependentNat *bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"` + UDPTimeout *int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"` } type tuicServerSchema struct { @@ -118,7 +117,7 @@ func pointerOrDefaultString(p *string, def string) string { return def } -func pointerOrDefaultTun(p *tunSchema, def sing_tun.Tun) sing_tun.Tun { +func pointerOrDefaultTun(p *tunSchema, def LC.Tun) LC.Tun { if p != nil { def.Enable = p.Enable if p.Device != nil { @@ -176,7 +175,7 @@ func pointerOrDefaultTun(p *tunSchema, def sing_tun.Tun) sing_tun.Tun { return def } -func pointerOrDefaultTuicServer(p *tuicServerSchema, def tuic.TuicServer) tuic.TuicServer { +func pointerOrDefaultTuicServer(p *tuicServerSchema, def LC.TuicServer) LC.TuicServer { if p != nil { def.Enable = p.Enable if p.Listen != nil { diff --git a/hub/route/server.go b/hub/route/server.go index 00a33a08..2bef92c1 100644 --- a/hub/route/server.go +++ b/hub/route/server.go @@ -9,9 +9,9 @@ import ( "time" "github.com/Dreamacro/clash/adapter/inbound" + CN "github.com/Dreamacro/clash/common/net" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" - CN "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/tunnel/statistic" "github.com/go-chi/chi/v5" diff --git a/listener/config/tuic.go b/listener/config/tuic.go new file mode 100644 index 00000000..78252fd3 --- /dev/null +++ b/listener/config/tuic.go @@ -0,0 +1,23 @@ +package config + +import ( + "encoding/json" +) + +type TuicServer struct { + Enable bool + Listen string + Token []string + Certificate string + PrivateKey string + CongestionController string + MaxIdleTime int + AuthenticationTimeout int + ALPN []string + MaxUdpRelayPacketSize int +} + +func (t TuicServer) String() string { + b, _ := json.Marshal(t) + return string(b) +} diff --git a/listener/sing_tun/tun.go b/listener/config/tun.go similarity index 99% rename from listener/sing_tun/tun.go rename to listener/config/tun.go index 95ff61ba..828ed488 100644 --- a/listener/sing_tun/tun.go +++ b/listener/config/tun.go @@ -1,4 +1,4 @@ -package sing_tun +package config import ( "encoding/json" diff --git a/listener/http/server.go b/listener/http/server.go index fceafb68..7bcc542f 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -9,10 +9,10 @@ import ( ) type Listener struct { - listener net.Listener - addr string - closed bool - name string + listener net.Listener + addr string + closed bool + name string preferRulesName string } @@ -33,14 +33,14 @@ func (l *Listener) Close() error { } func New(addr string, in chan<- C.ConnContext) (*Listener, error) { - return NewWithAuthenticate(addr,"DEFAULT-HTTP","", in, true) + return NewWithAuthenticate(addr, "DEFAULT-HTTP", "", in, true) } -func NewWithInfos(addr ,name ,preferRulesName string,in chan<-C.ConnContext)(*Listener,error){ - return NewWithAuthenticate(addr,name,preferRulesName,in,true) +func NewWithInfos(addr, name, preferRulesName string, in chan<- C.ConnContext) (*Listener, error) { + return NewWithAuthenticate(addr, name, preferRulesName, in, true) } -func NewWithAuthenticate(addr,name,preferRulesName string, in chan<- C.ConnContext, authenticate bool) (*Listener, error) { +func NewWithAuthenticate(addr, name, preferRulesName string, in chan<- C.ConnContext, authenticate bool) (*Listener, error) { l, err := inbound.Listen("tcp", addr) if err != nil { @@ -53,10 +53,10 @@ func NewWithAuthenticate(addr,name,preferRulesName string, in chan<- C.ConnConte } hl := &Listener{ - listener: l, - name: name, + listener: l, + name: name, preferRulesName: preferRulesName, - addr: addr, + addr: addr, } go func() { for { @@ -67,7 +67,7 @@ func NewWithAuthenticate(addr,name,preferRulesName string, in chan<- C.ConnConte } continue } - go HandleConn(hl.name,hl.preferRulesName,conn, in, c) + go HandleConn(hl.name, hl.preferRulesName, conn, in, c) } }() diff --git a/listener/inbound/base.go b/listener/inbound/base.go index 9fec28cd..a00ec5e6 100644 --- a/listener/inbound/base.go +++ b/listener/inbound/base.go @@ -52,7 +52,7 @@ func (b *Base) RawAddress() string { } // ReCreate implements constant.NewListener -func (*Base) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) error { +func (*Base) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { return nil } diff --git a/listener/inbound/http.go b/listener/inbound/http.go index 1902109f..3b3de193 100644 --- a/listener/inbound/http.go +++ b/listener/inbound/http.go @@ -30,7 +30,7 @@ func (h *HTTP) Address() string { } // ReCreate implements constant.NewListener -func (h *HTTP) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) error { +func (h *HTTP) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error _ = h.Close() h.l, err = http.NewWithInfos(h.RawAddress(), h.name, h.preferRulesName, tcpIn) diff --git a/listener/inbound/mixed.go b/listener/inbound/mixed.go index 691b2503..087c69fe 100644 --- a/listener/inbound/mixed.go +++ b/listener/inbound/mixed.go @@ -39,7 +39,7 @@ func (m *Mixed) Address() string { } // ReCreate implements constant.NewListener -func (m *Mixed) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) error { +func (m *Mixed) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error _ = m.Close() m.l, err = mixed.NewWithInfos(m.RawAddress(), m.name, m.preferRulesName, tcpIn) diff --git a/listener/inbound/redir.go b/listener/inbound/redir.go index baf6c0f7..a5864464 100644 --- a/listener/inbound/redir.go +++ b/listener/inbound/redir.go @@ -31,7 +31,7 @@ func (r *Redir) Address() string { } // ReCreate implements constant.NewListener -func (r *Redir) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) error { +func (r *Redir) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error _ = r.Close() r.l, err = redir.NewWithInfos(r.Address(), r.name, r.preferRulesName, tcpIn) diff --git a/listener/inbound/socks.go b/listener/inbound/socks.go index 35e11297..a1b44a24 100644 --- a/listener/inbound/socks.go +++ b/listener/inbound/socks.go @@ -60,7 +60,7 @@ func (s *Socks) Address() string { } // ReCreate implements constant.NewListener -func (s *Socks) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) error { +func (s *Socks) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { s.mux.Lock() defer s.mux.Unlock() var err error diff --git a/listener/inbound/tproxy.go b/listener/inbound/tproxy.go index acca5c8e..396117d2 100644 --- a/listener/inbound/tproxy.go +++ b/listener/inbound/tproxy.go @@ -38,7 +38,7 @@ func (t *TProxy) Address() string { } // ReCreate implements constant.NewListener -func (t *TProxy) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) error { +func (t *TProxy) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error _ = t.Close() t.lTCP, err = tproxy.NewWithInfos(t.RawAddress(), t.name, t.preferRulesName, tcpIn) diff --git a/listener/listener.go b/listener/listener.go index 4bf82369..836bfb2a 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -12,6 +12,7 @@ import ( "github.com/Dreamacro/clash/component/ebpf" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/listener/autoredir" + LC "github.com/Dreamacro/clash/listener/config" "github.com/Dreamacro/clash/listener/http" "github.com/Dreamacro/clash/listener/mixed" "github.com/Dreamacro/clash/listener/redir" @@ -64,8 +65,8 @@ var ( autoRedirMux sync.Mutex tcMux sync.Mutex - LastTunConf sing_tun.Tun - LastTuicConf tuic.TuicServer + LastTunConf LC.Tun + LastTuicConf LC.TuicServer ) type Ports struct { @@ -78,18 +79,18 @@ type Ports struct { VmessConfig string `json:"vmess-config"` } -func GetTunConf() sing_tun.Tun { +func GetTunConf() LC.Tun { if tunLister == nil { - return sing_tun.Tun{ + return LC.Tun{ Enable: false, } } return tunLister.Config() } -func GetTuicConf() tuic.TuicServer { +func GetTuicConf() LC.TuicServer { if tuicListener == nil { - return tuic.TuicServer{Enable: false} + return LC.TuicServer{Enable: false} } return tuicListener.Config() } @@ -144,7 +145,7 @@ func ReCreateHTTP(port int, tcpIn chan<- C.ConnContext) { log.Infoln("HTTP proxy listening at: %s", httpListener.Address()) } -func ReCreateSocks(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) { +func ReCreateSocks(port int, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) { socksMux.Lock() defer socksMux.Unlock() @@ -203,7 +204,7 @@ func ReCreateSocks(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketA log.Infoln("SOCKS proxy listening at: %s", socksListener.Address()) } -func ReCreateRedir(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) { +func ReCreateRedir(port int, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) { redirMux.Lock() defer redirMux.Unlock() @@ -249,7 +250,7 @@ func ReCreateRedir(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketA log.Infoln("Redirect proxy listening at: %s", redirListener.Address()) } -func ReCreateShadowSocks(shadowSocksConfig string, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) { +func ReCreateShadowSocks(shadowSocksConfig string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) { ssMux.Lock() defer ssMux.Unlock() @@ -289,7 +290,7 @@ func ReCreateShadowSocks(shadowSocksConfig string, tcpIn chan<- C.ConnContext, u return } -func ReCreateVmess(vmessConfig string, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) { +func ReCreateVmess(vmessConfig string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) { vmessMux.Lock() defer vmessMux.Unlock() @@ -329,7 +330,7 @@ func ReCreateVmess(vmessConfig string, tcpIn chan<- C.ConnContext, udpIn chan<- return } -func ReCreateTuic(config tuic.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) { +func ReCreateTuic(config LC.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) { tuicMux.Lock() defer func() { LastTuicConf = config @@ -371,7 +372,7 @@ func ReCreateTuic(config tuic.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan return } -func ReCreateTProxy(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) { +func ReCreateTProxy(port int, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) { tproxyMux.Lock() defer tproxyMux.Unlock() @@ -417,7 +418,7 @@ func ReCreateTProxy(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *C.Packet log.Infoln("TProxy server listening at: %s", tproxyListener.Address()) } -func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) { +func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) { mixedMux.Lock() defer mixedMux.Unlock() @@ -472,7 +473,7 @@ func ReCreateMixed(port int, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketA log.Infoln("Mixed(http+socks) proxy listening at: %s", mixedListener.Address()) } -func ReCreateTun(tunConf sing_tun.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) { +func ReCreateTun(tunConf LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) { tunMux.Lock() defer func() { LastTunConf = tunConf @@ -536,7 +537,7 @@ func ReCreateRedirToTun(ifaceNames []string) { log.Infoln("Attached tc ebpf program to interfaces %v", tcProgram.RawNICs()) } -func ReCreateAutoRedir(ifaceNames []string, tcpIn chan<- C.ConnContext, _ chan<- *C.PacketAdapter) { +func ReCreateAutoRedir(ifaceNames []string, tcpIn chan<- C.ConnContext, _ chan<- C.PacketAdapter) { autoRedirMux.Lock() defer autoRedirMux.Unlock() @@ -592,7 +593,7 @@ func ReCreateAutoRedir(ifaceNames []string, tcpIn chan<- C.ConnContext, _ chan<- log.Infoln("Auto redirect proxy listening at: %s, attached tc ebpf program to interfaces %v", autoRedirListener.Address(), autoRedirProgram.RawNICs()) } -func PatchTunnel(tunnels []tunnel.Tunnel, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) { +func PatchTunnel(tunnels []tunnel.Tunnel, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) { tunnelMux.Lock() defer tunnelMux.Unlock() @@ -745,7 +746,7 @@ func genAddr(host string, port int, allowLan bool) string { return fmt.Sprintf("127.0.0.1:%d", port) } -func hasTunConfigChange(tunConf *sing_tun.Tun) bool { +func hasTunConfigChange(tunConf *LC.Tun) bool { if LastTunConf.Enable != tunConf.Enable || LastTunConf.Device != tunConf.Device || LastTunConf.Stack != tunConf.Stack || @@ -833,5 +834,5 @@ func Cleanup(wait bool) { tunLister.Close() tunLister = nil } - LastTunConf = sing_tun.Tun{} + LastTunConf = LC.Tun{} } diff --git a/listener/parse.go b/listener/parse.go index 026c7df6..ee85e26c 100644 --- a/listener/parse.go +++ b/listener/parse.go @@ -25,23 +25,38 @@ func ParseListener(mapping map[string]any) (C.NewListener, error) { switch proxyType { case "socks": socksOption := &IN.SocksOption{} - decoder.Decode(mapping, socksOption) + err = decoder.Decode(mapping, socksOption) + if err != nil { + return nil, err + } listener, err = IN.NewSocks(socksOption) case "http": httpOption := &IN.HTTPOption{} - decoder.Decode(mapping, httpOption) + err = decoder.Decode(mapping, httpOption) + if err != nil { + return nil, err + } listener, err = IN.NewHTTP(httpOption) case "tproxy": tproxyOption := &IN.TProxyOption{} - decoder.Decode(mapping, tproxyOption) + err = decoder.Decode(mapping, tproxyOption) + if err != nil { + return nil, err + } listener, err = IN.NewTProxy(tproxyOption) case "redir": redirOption := &IN.RedirOption{} - decoder.Decode(mapping, redirOption) + err = decoder.Decode(mapping, redirOption) + if err != nil { + return nil, err + } listener, err = IN.NewRedir(redirOption) case "mixed": mixedOption := &IN.MixedOption{} - decoder.Decode(mapping, mixedOption) + err = decoder.Decode(mapping, mixedOption) + if err != nil { + return nil, err + } listener, err = IN.NewMixed(mixedOption) default: return nil, fmt.Errorf("unsupport proxy type: %s", proxyType) diff --git a/listener/redir/tcp.go b/listener/redir/tcp.go index d55897cb..a9e9b5b8 100644 --- a/listener/redir/tcp.go +++ b/listener/redir/tcp.go @@ -8,10 +8,10 @@ import ( ) type Listener struct { - listener net.Listener - addr string - closed bool - name string + listener net.Listener + addr string + closed bool + name string preferRulesName string } @@ -32,18 +32,18 @@ func (l *Listener) Close() error { } func New(addr string, in chan<- C.ConnContext) (*Listener, error) { - return NewWithInfos(addr,"DEFAULT-REDIR","",in) + return NewWithInfos(addr, "DEFAULT-REDIR", "", in) } -func NewWithInfos(addr,name,preferRulesName string, in chan<- C.ConnContext) (*Listener, error) { +func NewWithInfos(addr, name, preferRulesName string, in chan<- C.ConnContext) (*Listener, error) { l, err := net.Listen("tcp", addr) if err != nil { return nil, err } rl := &Listener{ - listener: l, - addr: addr, - name: name, + listener: l, + addr: addr, + name: name, preferRulesName: preferRulesName, } @@ -56,18 +56,18 @@ func NewWithInfos(addr,name,preferRulesName string, in chan<- C.ConnContext) (*L } continue } - go handleRedir(rl.name,rl.preferRulesName,c, in) + go handleRedir(rl.name, rl.preferRulesName, c, in) } }() return rl, nil } -func handleRedir(name,preferRulesName string,conn net.Conn, in chan<- C.ConnContext) { +func handleRedir(name, preferRulesName string, conn net.Conn, in chan<- C.ConnContext) { target, err := parserPacket(conn) if err != nil { conn.Close() return } conn.(*net.TCPConn).SetKeepAlive(true) - in <- inbound.NewSocketWithInfos(target, conn, C.REDIR,name,preferRulesName) + in <- inbound.NewSocketWithInfos(target, conn, C.REDIR, name, preferRulesName) } diff --git a/listener/shadowsocks/tcp.go b/listener/shadowsocks/tcp.go index 74ffca59..ab235e18 100644 --- a/listener/shadowsocks/tcp.go +++ b/listener/shadowsocks/tcp.go @@ -21,7 +21,7 @@ type Listener struct { var _listener *Listener -func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) (*Listener, error) { +func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) (*Listener, error) { addr, cipher, password, err := ParseSSURL(config) if err != nil { return nil, err diff --git a/listener/shadowsocks/udp.go b/listener/shadowsocks/udp.go index 5ea17c6b..6357afa9 100644 --- a/listener/shadowsocks/udp.go +++ b/listener/shadowsocks/udp.go @@ -17,7 +17,7 @@ type UDPListener struct { closed bool } -func NewUDP(addr string, pickCipher core.Cipher, in chan<- *C.PacketAdapter) (*UDPListener, error) { +func NewUDP(addr string, pickCipher core.Cipher, in chan<- C.PacketAdapter) (*UDPListener, error) { l, err := net.ListenPacket("udp", addr) if err != nil { return nil, err @@ -53,7 +53,7 @@ func (l *UDPListener) Close() error { return l.packetConn.Close() } -func handleSocksUDP(pc net.PacketConn, in chan<- *C.PacketAdapter, buf []byte, addr net.Addr) { +func handleSocksUDP(pc net.PacketConn, in chan<- C.PacketAdapter, buf []byte, addr net.Addr) { tgtAddr := socks5.SplitAddr(buf) if tgtAddr == nil { // Unresolved UDP packet, return buffer to the pool diff --git a/listener/sing/sing.go b/listener/sing/sing.go index 4ff37f6a..5b605373 100644 --- a/listener/sing/sing.go +++ b/listener/sing/sing.go @@ -24,7 +24,7 @@ const UDPTimeout = 5 * time.Minute type ListenerHandler struct { TcpIn chan<- C.ConnContext - UdpIn chan<- *C.PacketAdapter + UdpIn chan<- C.PacketAdapter Type C.Type } diff --git a/listener/sing_shadowsocks/server.go b/listener/sing_shadowsocks/server.go index ecfc1df7..967d64fa 100644 --- a/listener/sing_shadowsocks/server.go +++ b/listener/sing_shadowsocks/server.go @@ -32,7 +32,7 @@ type Listener struct { var _listener *Listener -func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) (C.AdvanceListener, error) { +func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) (C.AdvanceListener, error) { addr, cipher, password, err := embedSS.ParseSSURL(config) if err != nil { return nil, err diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 13437149..aeba8999 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -11,6 +11,7 @@ import ( "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/iface" C "github.com/Dreamacro/clash/constant" + LC "github.com/Dreamacro/clash/listener/config" "github.com/Dreamacro/clash/listener/sing" "github.com/Dreamacro/clash/log" @@ -25,7 +26,7 @@ var InterfaceName = "Meta" type Listener struct { closed bool - options Tun + options LC.Tun handler *ListenerHandler tunName string @@ -63,7 +64,7 @@ func CalculateInterfaceName(name string) (tunName string) { return } -func New(options Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) (l *Listener, err error) { +func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) (l *Listener, err error) { tunName := options.Device if tunName == "" { tunName = CalculateInterfaceName(InterfaceName) @@ -161,12 +162,12 @@ func New(options Tun, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) tunOptions := tun.Options{ Name: tunName, MTU: tunMTU, - Inet4Address: common.Map(options.Inet4Address, ListenPrefix.Build), - Inet6Address: common.Map(options.Inet6Address, ListenPrefix.Build), + Inet4Address: common.Map(options.Inet4Address, LC.ListenPrefix.Build), + Inet6Address: common.Map(options.Inet6Address, LC.ListenPrefix.Build), AutoRoute: options.AutoRoute, StrictRoute: options.StrictRoute, - Inet4RouteAddress: common.Map(options.Inet4RouteAddress, ListenPrefix.Build), - Inet6RouteAddress: common.Map(options.Inet6RouteAddress, ListenPrefix.Build), + Inet4RouteAddress: common.Map(options.Inet4RouteAddress, LC.ListenPrefix.Build), + Inet6RouteAddress: common.Map(options.Inet6RouteAddress, LC.ListenPrefix.Build), IncludeUID: includeUID, ExcludeUID: excludeUID, IncludeAndroidUser: options.IncludeAndroidUser, @@ -282,6 +283,6 @@ func (l *Listener) Close() { ) } -func (l *Listener) Config() Tun { +func (l *Listener) Config() LC.Tun { return l.options } diff --git a/listener/sing_vmess/server.go b/listener/sing_vmess/server.go index 4dd046c1..997fc6b0 100644 --- a/listener/sing_vmess/server.go +++ b/listener/sing_vmess/server.go @@ -24,7 +24,7 @@ type Listener struct { var _listener *Listener -func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) (*Listener, error) { +func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) (*Listener, error) { addr, username, password, err := parseVmessURL(config) if err != nil { return nil, err diff --git a/listener/socks/tcp.go b/listener/socks/tcp.go index 7ddc71ea..30174b61 100644 --- a/listener/socks/tcp.go +++ b/listener/socks/tcp.go @@ -13,11 +13,11 @@ import ( ) type Listener struct { - listener net.Listener - addr string - closed bool - preferRulesName string - name string + listener net.Listener + addr string + closed bool + preferRulesName string + name string } // RawAddress implements C.Listener @@ -37,19 +37,19 @@ func (l *Listener) Close() error { } func New(addr string, in chan<- C.ConnContext) (*Listener, error) { - return NewWithInfos(addr,"DEFAULT-SOCKS","",in) + return NewWithInfos(addr, "DEFAULT-SOCKS", "", in) } -func NewWithInfos(addr,name,preferRulesName string, in chan<- C.ConnContext) (*Listener, error) { +func NewWithInfos(addr, name, preferRulesName string, in chan<- C.ConnContext) (*Listener, error) { l, err := inbound.Listen("tcp", addr) if err != nil { return nil, err } sl := &Listener{ - listener: l, - addr: addr, - name: name, + listener: l, + addr: addr, + name: name, preferRulesName: preferRulesName, } go func() { @@ -61,14 +61,14 @@ func NewWithInfos(addr,name,preferRulesName string, in chan<- C.ConnContext) (*L } continue } - go handleSocks(sl.name,sl.preferRulesName,c, in) + go handleSocks(sl.name, sl.preferRulesName, c, in) } }() return sl, nil } -func handleSocks(name,preferRulesName string,conn net.Conn, in chan<- C.ConnContext) { +func handleSocks(name, preferRulesName string, conn net.Conn, in chan<- C.ConnContext) { conn.(*net.TCPConn).SetKeepAlive(true) bufConn := N.NewBufferedConn(conn) head, err := bufConn.Peek(1) @@ -79,24 +79,24 @@ func handleSocks(name,preferRulesName string,conn net.Conn, in chan<- C.ConnCont switch head[0] { case socks4.Version: - HandleSocks4(name,preferRulesName,bufConn, in) + HandleSocks4(name, preferRulesName, bufConn, in) case socks5.Version: - HandleSocks5(name,preferRulesName,bufConn, in) + HandleSocks5(name, preferRulesName, bufConn, in) default: conn.Close() } } -func HandleSocks4(name,preferRulesName string, conn net.Conn, in chan<- C.ConnContext) { +func HandleSocks4(name, preferRulesName string, conn net.Conn, in chan<- C.ConnContext) { addr, _, err := socks4.ServerHandshake(conn, authStore.Authenticator()) if err != nil { conn.Close() return } - in <- inbound.NewSocketWithInfos(socks5.ParseAddr(addr), conn, C.SOCKS4,name,preferRulesName) + in <- inbound.NewSocketWithInfos(socks5.ParseAddr(addr), conn, C.SOCKS4, name, preferRulesName) } -func HandleSocks5(name,preferRulesName string,conn net.Conn, in chan<- C.ConnContext) { +func HandleSocks5(name, preferRulesName string, conn net.Conn, in chan<- C.ConnContext) { target, command, err := socks5.ServerHandshake(conn, authStore.Authenticator()) if err != nil { conn.Close() @@ -107,5 +107,5 @@ func HandleSocks5(name,preferRulesName string,conn net.Conn, in chan<- C.ConnCon io.Copy(io.Discard, conn) return } - in <- inbound.NewSocketWithInfos(target, conn, C.SOCKS5,name,preferRulesName) + in <- inbound.NewSocketWithInfos(target, conn, C.SOCKS5, name, preferRulesName) } diff --git a/listener/socks/udp.go b/listener/socks/udp.go index 6adbd257..cde92c54 100644 --- a/listener/socks/udp.go +++ b/listener/socks/udp.go @@ -12,10 +12,10 @@ import ( ) type UDPListener struct { - packetConn net.PacketConn - addr string - closed bool - name string + packetConn net.PacketConn + addr string + closed bool + name string preferRulesName string } @@ -35,11 +35,11 @@ func (l *UDPListener) Close() error { return l.packetConn.Close() } -func NewUDP(addr string, in chan<- *C.PacketAdapter) (*UDPListener, error) { - return NewUDPWithInfos(addr,"DEFAULT-SOCKS","",in) +func NewUDP(addr string, in chan<- C.PacketAdapter) (*UDPListener, error) { + return NewUDPWithInfos(addr, "DEFAULT-SOCKS", "", in) } -func NewUDPWithInfos(addr,name ,preferRulesName string, in chan<- *C.PacketAdapter) (*UDPListener, error) { +func NewUDPWithInfos(addr, name, preferRulesName string, in chan<- C.PacketAdapter) (*UDPListener, error) { l, err := net.ListenPacket("udp", addr) if err != nil { return nil, err @@ -50,10 +50,10 @@ func NewUDPWithInfos(addr,name ,preferRulesName string, in chan<- *C.PacketAdapt } sl := &UDPListener{ - packetConn: l, - addr: addr, + packetConn: l, + addr: addr, preferRulesName: preferRulesName, - name: name, + name: name, } go func() { for { @@ -66,14 +66,14 @@ func NewUDPWithInfos(addr,name ,preferRulesName string, in chan<- *C.PacketAdapt } continue } - handleSocksUDP(sl.name,sl.preferRulesName,l, in, buf[:n], remoteAddr) + handleSocksUDP(sl.name, sl.preferRulesName, l, in, buf[:n], remoteAddr) } }() return sl, nil } -func handleSocksUDP(name,preferRulesName string,pc net.PacketConn, in chan<- *C.PacketAdapter, buf []byte, addr net.Addr) { +func handleSocksUDP(name, preferRulesName string, pc net.PacketConn, in chan<- C.PacketAdapter, buf []byte, addr net.Addr) { target, payload, err := socks5.DecodeUDPPacket(buf) if err != nil { // Unresolved UDP packet, return buffer to the pool @@ -87,7 +87,7 @@ func handleSocksUDP(name,preferRulesName string,pc net.PacketConn, in chan<- *C. bufRef: buf, } select { - case in <- inbound.NewPacketWithInfos(target, packet, C.SOCKS5,name,preferRulesName): + case in <- inbound.NewPacketWithInfos(target, packet, C.SOCKS5, name, preferRulesName): default: } } diff --git a/listener/tproxy/tproxy.go b/listener/tproxy/tproxy.go index c546008d..a1c53b53 100644 --- a/listener/tproxy/tproxy.go +++ b/listener/tproxy/tproxy.go @@ -9,10 +9,10 @@ import ( ) type Listener struct { - listener net.Listener - addr string - closed bool - name string + listener net.Listener + addr string + closed bool + name string preferRulesName string } @@ -32,17 +32,17 @@ func (l *Listener) Close() error { return l.listener.Close() } -func (l *Listener) handleTProxy(name,preferRulesName string ,conn net.Conn, in chan<- C.ConnContext) { +func (l *Listener) handleTProxy(name, preferRulesName string, conn net.Conn, in chan<- C.ConnContext) { target := socks5.ParseAddrToSocksAddr(conn.LocalAddr()) conn.(*net.TCPConn).SetKeepAlive(true) - in <- inbound.NewSocketWithInfos(target, conn, C.TPROXY,name,preferRulesName) + in <- inbound.NewSocketWithInfos(target, conn, C.TPROXY, name, preferRulesName) } func New(addr string, in chan<- C.ConnContext) (*Listener, error) { - return NewWithInfos(addr,"DEFAULT-TPROXY","",in) + return NewWithInfos(addr, "DEFAULT-TPROXY", "", in) } -func NewWithInfos(addr,name,preferRulesName string, in chan<- C.ConnContext) (*Listener, error) { +func NewWithInfos(addr, name, preferRulesName string, in chan<- C.ConnContext) (*Listener, error) { l, err := net.Listen("tcp", addr) if err != nil { return nil, err @@ -60,9 +60,9 @@ func NewWithInfos(addr,name,preferRulesName string, in chan<- C.ConnContext) (*L } rl := &Listener{ - listener: l, - addr: addr, - name: name, + listener: l, + addr: addr, + name: name, preferRulesName: preferRulesName, } @@ -75,7 +75,7 @@ func NewWithInfos(addr,name,preferRulesName string, in chan<- C.ConnContext) (*L } continue } - go rl.handleTProxy(rl.name,rl.preferRulesName,c, in) + go rl.handleTProxy(rl.name, rl.preferRulesName, c, in) } }() diff --git a/listener/tproxy/udp.go b/listener/tproxy/udp.go index f34e5525..9560a626 100644 --- a/listener/tproxy/udp.go +++ b/listener/tproxy/udp.go @@ -34,11 +34,11 @@ func (l *UDPListener) Close() error { return l.packetConn.Close() } -func NewUDP(addr string, in chan<- *C.PacketAdapter) (*UDPListener, error) { +func NewUDP(addr string, in chan<- C.PacketAdapter) (*UDPListener, error) { return NewUDPWithInfos(addr, "DEFAULT-TPROXY", "", in) } -func NewUDPWithInfos(addr, name, preferRulesName string, in chan<- *C.PacketAdapter) (*UDPListener, error) { +func NewUDPWithInfos(addr, name, preferRulesName string, in chan<- C.PacketAdapter) (*UDPListener, error) { l, err := net.ListenPacket("udp", addr) if err != nil { return nil, err @@ -90,7 +90,7 @@ func NewUDPWithInfos(addr, name, preferRulesName string, in chan<- *C.PacketAdap return rl, nil } -func handlePacketConn(name, preferRulesName string, pc net.PacketConn, in chan<- *C.PacketAdapter, buf []byte, lAddr, rAddr netip.AddrPort) { +func handlePacketConn(name, preferRulesName string, pc net.PacketConn, in chan<- C.PacketAdapter, buf []byte, lAddr, rAddr netip.AddrPort) { target := socks5.AddrFromStdAddrPort(rAddr) pkt := &packet{ pc: pc, diff --git a/listener/tuic/server.go b/listener/tuic/server.go index 565f258e..82f208cb 100644 --- a/listener/tuic/server.go +++ b/listener/tuic/server.go @@ -2,47 +2,29 @@ package tuic import ( "crypto/tls" - "encoding/json" "net" "strings" "time" "github.com/metacubex/quic-go" + "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/common/sockopt" C "github.com/Dreamacro/clash/constant" - "github.com/Dreamacro/clash/adapter/inbound" + LC "github.com/Dreamacro/clash/listener/config" "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/transport/socks5" "github.com/Dreamacro/clash/transport/tuic" ) -type TuicServer struct { - Enable bool - Listen string - Token []string - Certificate string - PrivateKey string - CongestionController string - MaxIdleTime int - AuthenticationTimeout int - ALPN []string - MaxUdpRelayPacketSize int -} - -func (t TuicServer) String() string { - b, _ := json.Marshal(t) - return string(b) -} - type Listener struct { closed bool - config TuicServer + config LC.TuicServer udpListeners []net.PacketConn servers []*tuic.Server } -func New(config TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- *C.PacketAdapter) (*Listener, error) { +func New(config LC.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) (*Listener, error) { cert, err := tls.LoadX509KeyPair(config.Certificate, config.PrivateKey) if err != nil { return nil, err @@ -140,6 +122,6 @@ func (l *Listener) Close() { } } -func (l *Listener) Config() TuicServer { +func (l *Listener) Config() LC.TuicServer { return l.config } diff --git a/listener/tunnel/udp.go b/listener/tunnel/udp.go index 2f8ddeb6..506e990f 100644 --- a/listener/tunnel/udp.go +++ b/listener/tunnel/udp.go @@ -34,7 +34,7 @@ func (l *PacketConn) Close() error { return l.conn.Close() } -func NewUDP(addr, target, proxy string, in chan<- *C.PacketAdapter) (*PacketConn, error) { +func NewUDP(addr, target, proxy string, in chan<- C.PacketAdapter) (*PacketConn, error) { l, err := net.ListenPacket("udp", addr) if err != nil { return nil, err @@ -69,7 +69,7 @@ func NewUDP(addr, target, proxy string, in chan<- *C.PacketAdapter) (*PacketConn return sl, nil } -func (l *PacketConn) handleUDP(pc net.PacketConn, in chan<- *C.PacketAdapter, buf []byte, addr net.Addr) { +func (l *PacketConn) handleUDP(pc net.PacketConn, in chan<- C.PacketAdapter, buf []byte, addr net.Addr) { packet := &packet{ pc: pc, rAddr: addr, diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 6a0af92e..99fcb33b 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -26,7 +26,7 @@ import ( var ( tcpQueue = make(chan C.ConnContext, 200) - udpQueue = make(chan *C.PacketAdapter, 200) + udpQueue = make(chan C.PacketAdapter, 200) natTable = nat.New() rules []C.Rule subRules map[string][]C.Rule @@ -77,7 +77,7 @@ func TCPIn() chan<- C.ConnContext { } // UDPIn return fan-in udp queue -func UDPIn() chan<- *C.PacketAdapter { +func UDPIn() chan<- C.PacketAdapter { return udpQueue } @@ -217,7 +217,7 @@ func resolveMetadata(ctx C.PlainContext, metadata *C.Metadata) (proxy C.Proxy, r return } -func handleUDPConn(packet *C.PacketAdapter) { +func handleUDPConn(packet C.PacketAdapter) { metadata := packet.Metadata() if !metadata.Valid() { log.Warnln("[Metadata] not valid: %#v", metadata) @@ -325,7 +325,7 @@ func handleUDPConn(packet *C.PacketAdapter) { } oAddr := metadata.DstIP - go handleUDPToLocal(packet.UDPPacket, pc, key, oAddr, fAddr) + go handleUDPToLocal(packet, pc, key, oAddr, fAddr) natTable.Set(key, pc) handle() From e9d8dd09ac69ba3ef9a766335a731d1e6d7cee93 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Sun, 4 Dec 2022 15:15:23 +0800 Subject: [PATCH 193/250] fix: close linstener --- constant/listener.go | 2 +- hub/executor/executor.go | 7 ++++++- listener/inbound/base.go | 2 +- listener/inbound/http.go | 3 +-- listener/inbound/mixed.go | 3 +-- listener/inbound/redir.go | 3 +-- listener/inbound/socks.go | 8 +------- listener/inbound/tproxy.go | 3 +-- tunnel/tunnel.go | 10 ++++++++++ 9 files changed, 23 insertions(+), 18 deletions(-) diff --git a/constant/listener.go b/constant/listener.go index 4edae67c..c5e327ec 100644 --- a/constant/listener.go +++ b/constant/listener.go @@ -16,7 +16,7 @@ type AdvanceListener interface { type NewListener interface { Name() string - ReCreate(tcpIn chan<- ConnContext, udpIn chan<- PacketAdapter) error + Listen(tcpIn chan<- ConnContext, udpIn chan<- PacketAdapter) error Close() error Address() string RawAddress() string diff --git a/hub/executor/executor.go b/hub/executor/executor.go index e1ac7f6e..9a1003ef 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -138,11 +138,16 @@ func GetGeneral() *config.General { func updateListeners(listeners map[string]C.NewListener) { tcpIn := tunnel.TCPIn() udpIn := tunnel.UDPIn() + for _, listener := range tunnel.Listeners() { + _ = listener.Close() + } + for _, listener := range listeners { - if err := listener.ReCreate(tcpIn, udpIn); err != nil { + if err := listener.Listen(tcpIn, udpIn); err != nil { log.Errorln("Listener %s listen err: %s", listener.Name(), err.Error()) } } + tunnel.UpdateListeners(listeners) } func updateExperimental(c *config.Config) { diff --git a/listener/inbound/base.go b/listener/inbound/base.go index a00ec5e6..10008843 100644 --- a/listener/inbound/base.go +++ b/listener/inbound/base.go @@ -52,7 +52,7 @@ func (b *Base) RawAddress() string { } // ReCreate implements constant.NewListener -func (*Base) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { +func (*Base) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { return nil } diff --git a/listener/inbound/http.go b/listener/inbound/http.go index 3b3de193..1092d2f9 100644 --- a/listener/inbound/http.go +++ b/listener/inbound/http.go @@ -30,9 +30,8 @@ func (h *HTTP) Address() string { } // ReCreate implements constant.NewListener -func (h *HTTP) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { +func (h *HTTP) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error - _ = h.Close() h.l, err = http.NewWithInfos(h.RawAddress(), h.name, h.preferRulesName, tcpIn) if err != nil { return err diff --git a/listener/inbound/mixed.go b/listener/inbound/mixed.go index 087c69fe..949064fe 100644 --- a/listener/inbound/mixed.go +++ b/listener/inbound/mixed.go @@ -39,9 +39,8 @@ func (m *Mixed) Address() string { } // ReCreate implements constant.NewListener -func (m *Mixed) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { +func (m *Mixed) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error - _ = m.Close() m.l, err = mixed.NewWithInfos(m.RawAddress(), m.name, m.preferRulesName, tcpIn) if err != nil { return err diff --git a/listener/inbound/redir.go b/listener/inbound/redir.go index a5864464..029e103a 100644 --- a/listener/inbound/redir.go +++ b/listener/inbound/redir.go @@ -31,9 +31,8 @@ func (r *Redir) Address() string { } // ReCreate implements constant.NewListener -func (r *Redir) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { +func (r *Redir) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error - _ = r.Close() r.l, err = redir.NewWithInfos(r.Address(), r.name, r.preferRulesName, tcpIn) if err != nil { return err diff --git a/listener/inbound/socks.go b/listener/inbound/socks.go index a1b44a24..1aa3c23f 100644 --- a/listener/inbound/socks.go +++ b/listener/inbound/socks.go @@ -2,8 +2,6 @@ package inbound import ( "fmt" - "sync" - C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/listener/socks" "github.com/Dreamacro/clash/log" @@ -16,7 +14,6 @@ type SocksOption struct { type Socks struct { *Base - mux sync.Mutex udp bool stl *socks.Listener sul *socks.UDPListener @@ -60,11 +57,8 @@ func (s *Socks) Address() string { } // ReCreate implements constant.NewListener -func (s *Socks) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { - s.mux.Lock() - defer s.mux.Unlock() +func (s *Socks) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error - _ = s.Close() if s.stl, err = socks.NewWithInfos(s.RawAddress(), s.name, s.preferRulesName, tcpIn); err != nil { return err } diff --git a/listener/inbound/tproxy.go b/listener/inbound/tproxy.go index 396117d2..00d9c555 100644 --- a/listener/inbound/tproxy.go +++ b/listener/inbound/tproxy.go @@ -38,9 +38,8 @@ func (t *TProxy) Address() string { } // ReCreate implements constant.NewListener -func (t *TProxy) ReCreate(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { +func (t *TProxy) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error - _ = t.Close() t.lTCP, err = tproxy.NewWithInfos(t.RawAddress(), t.name, t.preferRulesName, tcpIn) if err != nil { return err diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 99fcb33b..2c8d37e0 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -29,6 +29,7 @@ var ( udpQueue = make(chan C.PacketAdapter, 200) natTable = nat.New() rules []C.Rule + listeners = make(map[string]C.NewListener) subRules map[string][]C.Rule proxies = make(map[string]C.Proxy) providers map[string]provider.ProxyProvider @@ -86,6 +87,9 @@ func Rules() []C.Rule { return rules } +func Listeners()map[string]C.NewListener{ + return listeners +} // UpdateRules handle update rules func UpdateRules(newRules []C.Rule, newSubRule map[string][]C.Rule, rp map[string]provider.RuleProvider) { configMux.Lock() @@ -118,6 +122,12 @@ func UpdateProxies(newProxies map[string]C.Proxy, newProviders map[string]provid configMux.Unlock() } +func UpdateListeners(newListeners map[string]C.NewListener) { + configMux.Lock() + defer configMux.Unlock() + listeners=newListeners +} + func UpdateSniffer(dispatcher *sniffer.SnifferDispatcher) { configMux.Lock() sniffer.Dispatcher = dispatcher From 8144373725dc74c3d44f0324dbeb16131211219a Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 4 Dec 2022 17:20:24 +0800 Subject: [PATCH 194/250] chore: support skip the same config Listeners' Close and Listen --- constant/listener.go | 1 + hub/executor/executor.go | 22 +++++++++++++++++----- listener/inbound/base.go | 15 ++++++++++++++- listener/inbound/http.go | 13 ++++++++++--- listener/inbound/mixed.go | 19 +++++++++++++------ listener/inbound/redir.go | 13 ++++++++++--- listener/inbound/socks.go | 19 +++++++++++++------ listener/inbound/tproxy.go | 19 +++++++++++++------ tunnel/tunnel.go | 5 +++-- 9 files changed, 94 insertions(+), 32 deletions(-) diff --git a/constant/listener.go b/constant/listener.go index c5e327ec..a6696af4 100644 --- a/constant/listener.go +++ b/constant/listener.go @@ -20,4 +20,5 @@ type NewListener interface { Close() error Address() string RawAddress() string + Config() string } diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 9a1003ef..94db8c0f 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -138,13 +138,25 @@ func GetGeneral() *config.General { func updateListeners(listeners map[string]C.NewListener) { tcpIn := tunnel.TCPIn() udpIn := tunnel.UDPIn() - for _, listener := range tunnel.Listeners() { - _ = listener.Close() + + skipNames := map[string]struct{}{} + for name, oldListener := range tunnel.Listeners() { + if newListener, ok := listeners[name]; ok { + if newListener.Config() == oldListener.Config() { + listeners[name] = oldListener + skipNames[name] = struct{}{} + continue + } + } + _ = oldListener.Close() } - for _, listener := range listeners { - if err := listener.Listen(tcpIn, udpIn); err != nil { - log.Errorln("Listener %s listen err: %s", listener.Name(), err.Error()) + for name, newListener := range listeners { + if _, ok := skipNames[name]; ok { + continue + } + if err := newListener.Listen(tcpIn, udpIn); err != nil { + log.Errorln("Listener %s listen err: %s", newListener.Name(), err.Error()) } } tunnel.UpdateListeners(listeners) diff --git a/listener/inbound/base.go b/listener/inbound/base.go index 10008843..ebe65309 100644 --- a/listener/inbound/base.go +++ b/listener/inbound/base.go @@ -1,6 +1,7 @@ package inbound import ( + "encoding/json" "net" "net/netip" "strconv" @@ -9,6 +10,7 @@ import ( ) type Base struct { + config *BaseOption name string preferRulesName string listenAddr netip.Addr @@ -28,9 +30,15 @@ func NewBase(options *BaseOption) (*Base, error) { listenAddr: addr, preferRulesName: options.PreferRulesName, port: options.Port, + config: options, }, nil } +// Config implements constant.NewListener +func (b *Base) Config() string { + return optionToString(b.config) +} + // Address implements constant.NewListener func (b *Base) Address() string { return b.RawAddress() @@ -51,7 +59,7 @@ func (b *Base) RawAddress() string { return net.JoinHostPort(b.listenAddr.String(), strconv.Itoa(int(b.port))) } -// ReCreate implements constant.NewListener +// Listen implements constant.NewListener func (*Base) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { return nil } @@ -64,3 +72,8 @@ type BaseOption struct { } var _ C.NewListener = (*Base)(nil) + +func optionToString(option any) string { + str, _ := json.Marshal(option) + return string(str) +} diff --git a/listener/inbound/http.go b/listener/inbound/http.go index 1092d2f9..6f04b4b6 100644 --- a/listener/inbound/http.go +++ b/listener/inbound/http.go @@ -11,7 +11,8 @@ type HTTPOption struct { } type HTTP struct { *Base - l *http.Listener + config *HTTPOption + l *http.Listener } func NewHTTP(options *HTTPOption) (*HTTP, error) { @@ -20,16 +21,22 @@ func NewHTTP(options *HTTPOption) (*HTTP, error) { return nil, err } return &HTTP{ - Base: base, + Base: base, + config: options, }, nil } +// Config implements constant.NewListener +func (h *HTTP) Config() string { + return optionToString(h.config) +} + // Address implements constant.NewListener func (h *HTTP) Address() string { return h.l.Address() } -// ReCreate implements constant.NewListener +// Listen implements constant.NewListener func (h *HTTP) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error h.l, err = http.NewWithInfos(h.RawAddress(), h.name, h.preferRulesName, tcpIn) diff --git a/listener/inbound/mixed.go b/listener/inbound/mixed.go index 949064fe..01120dd7 100644 --- a/listener/inbound/mixed.go +++ b/listener/inbound/mixed.go @@ -17,9 +17,10 @@ type MixedOption struct { type Mixed struct { *Base - l *mixed.Listener - lUDP *socks.UDPListener - udp bool + config *MixedOption + l *mixed.Listener + lUDP *socks.UDPListener + udp bool } func NewMixed(options *MixedOption) (*Mixed, error) { @@ -28,17 +29,23 @@ func NewMixed(options *MixedOption) (*Mixed, error) { return nil, err } return &Mixed{ - Base: base, - udp: options.UDP == nil || *options.UDP, + Base: base, + config: options, + udp: options.UDP == nil || *options.UDP, }, nil } +// Config implements constant.NewListener +func (m *Mixed) Config() string { + return optionToString(m.config) +} + // Address implements constant.NewListener func (m *Mixed) Address() string { return m.l.Address() } -// ReCreate implements constant.NewListener +// Listen implements constant.NewListener func (m *Mixed) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error m.l, err = mixed.NewWithInfos(m.RawAddress(), m.name, m.preferRulesName, tcpIn) diff --git a/listener/inbound/redir.go b/listener/inbound/redir.go index 029e103a..6d6ca4f0 100644 --- a/listener/inbound/redir.go +++ b/listener/inbound/redir.go @@ -12,7 +12,8 @@ type RedirOption struct { type Redir struct { *Base - l *redir.Listener + config *RedirOption + l *redir.Listener } func NewRedir(options *RedirOption) (*Redir, error) { @@ -21,16 +22,22 @@ func NewRedir(options *RedirOption) (*Redir, error) { return nil, err } return &Redir{ - Base: base, + Base: base, + config: options, }, nil } +// Config implements constant.NewListener +func (r *Redir) Config() string { + return optionToString(r.config) +} + // Address implements constant.NewListener func (r *Redir) Address() string { return r.l.Address() } -// ReCreate implements constant.NewListener +// Listen implements constant.NewListener func (r *Redir) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error r.l, err = redir.NewWithInfos(r.Address(), r.name, r.preferRulesName, tcpIn) diff --git a/listener/inbound/socks.go b/listener/inbound/socks.go index 1aa3c23f..9e826bc3 100644 --- a/listener/inbound/socks.go +++ b/listener/inbound/socks.go @@ -14,9 +14,10 @@ type SocksOption struct { type Socks struct { *Base - udp bool - stl *socks.Listener - sul *socks.UDPListener + config *SocksOption + udp bool + stl *socks.Listener + sul *socks.UDPListener } func NewSocks(options *SocksOption) (*Socks, error) { @@ -25,11 +26,17 @@ func NewSocks(options *SocksOption) (*Socks, error) { return nil, err } return &Socks{ - Base: base, - udp: options.UDP == nil || *options.UDP, + Base: base, + config: options, + udp: options.UDP == nil || *options.UDP, }, nil } +// Config implements constant.NewListener +func (s *Socks) Config() string { + return optionToString(s.config) +} + // Close implements constant.NewListener func (s *Socks) Close() error { var err error @@ -56,7 +63,7 @@ func (s *Socks) Address() string { return s.stl.Address() } -// ReCreate implements constant.NewListener +// Listen implements constant.NewListener func (s *Socks) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error if s.stl, err = socks.NewWithInfos(s.RawAddress(), s.name, s.preferRulesName, tcpIn); err != nil { diff --git a/listener/inbound/tproxy.go b/listener/inbound/tproxy.go index 00d9c555..3a86919c 100644 --- a/listener/inbound/tproxy.go +++ b/listener/inbound/tproxy.go @@ -15,9 +15,10 @@ type TProxyOption struct { type TProxy struct { *Base - lUDP *tproxy.UDPListener - lTCP *tproxy.Listener - udp bool + config *TProxyOption + lUDP *tproxy.UDPListener + lTCP *tproxy.Listener + udp bool } func NewTProxy(options *TProxyOption) (*TProxy, error) { @@ -26,18 +27,24 @@ func NewTProxy(options *TProxyOption) (*TProxy, error) { return nil, err } return &TProxy{ - Base: base, - udp: options.UDP == nil || *options.UDP, + Base: base, + config: options, + udp: options.UDP == nil || *options.UDP, }, nil } +// Config implements constant.NewListener +func (t *TProxy) Config() string { + return optionToString(t.config) +} + // Address implements constant.NewListener func (t *TProxy) Address() string { return t.lTCP.Address() } -// ReCreate implements constant.NewListener +// Listen implements constant.NewListener func (t *TProxy) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error t.lTCP, err = tproxy.NewWithInfos(t.RawAddress(), t.name, t.preferRulesName, tcpIn) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 2c8d37e0..3be2f4aa 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -87,9 +87,10 @@ func Rules() []C.Rule { return rules } -func Listeners()map[string]C.NewListener{ +func Listeners() map[string]C.NewListener { return listeners } + // UpdateRules handle update rules func UpdateRules(newRules []C.Rule, newSubRule map[string][]C.Rule, rp map[string]provider.RuleProvider) { configMux.Lock() @@ -125,7 +126,7 @@ func UpdateProxies(newProxies map[string]C.Proxy, newProviders map[string]provid func UpdateListeners(newListeners map[string]C.NewListener) { configMux.Lock() defer configMux.Unlock() - listeners=newListeners + listeners = newListeners } func UpdateSniffer(dispatcher *sniffer.SnifferDispatcher) { From 62226e8b3db5707dc126aa3be507ba6e4df6ecb9 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 4 Dec 2022 21:53:13 +0800 Subject: [PATCH 195/250] chore: rebuild InboundListener --- adapter/parser.go | 6 +----- common/structure/structure.go | 2 ++ config/config.go | 6 +++--- constant/listener.go | 9 +++++++-- hub/executor/executor.go | 24 ++---------------------- listener/inbound/base.go | 32 +++++++++++++++++++++----------- listener/inbound/http.go | 19 ++++++++++++------- listener/inbound/mixed.go | 22 +++++++++++++--------- listener/inbound/redir.go | 18 +++++++++++------- listener/inbound/socks.go | 22 +++++++++++++--------- listener/inbound/tproxy.go | 22 +++++++++++++--------- listener/listener.go | 31 +++++++++++++++++++++++++++++++ listener/parse.go | 15 ++++++--------- tunnel/tunnel.go | 6 +++--- 14 files changed, 138 insertions(+), 96 deletions(-) diff --git a/adapter/parser.go b/adapter/parser.go index 3ca0c3ee..86fe96f9 100644 --- a/adapter/parser.go +++ b/adapter/parser.go @@ -2,17 +2,13 @@ package adapter import ( "fmt" - "strings" - "github.com/Dreamacro/clash/adapter/outbound" "github.com/Dreamacro/clash/common/structure" C "github.com/Dreamacro/clash/constant" ) -var keyReplacer = strings.NewReplacer("_", "-") - func ParseProxy(mapping map[string]any) (C.Proxy, error) { - decoder := structure.NewDecoder(structure.Option{TagName: "proxy", WeaklyTypedInput: true, KeyReplacer: keyReplacer}) + decoder := structure.NewDecoder(structure.Option{TagName: "proxy", WeaklyTypedInput: true, KeyReplacer: structure.DefaultKeyReplacer}) proxyType, existType := mapping["type"].(string) if !existType { return nil, fmt.Errorf("missing type") diff --git a/common/structure/structure.go b/common/structure/structure.go index 958ad54b..c2ae393d 100644 --- a/common/structure/structure.go +++ b/common/structure/structure.go @@ -16,6 +16,8 @@ type Option struct { KeyReplacer *strings.Replacer } +var DefaultKeyReplacer = strings.NewReplacer("_", "-") + // Decoder is the core of structure type Decoder struct { option *Option diff --git a/config/config.go b/config/config.go index c44ac250..0dc100f4 100644 --- a/config/config.go +++ b/config/config.go @@ -200,7 +200,7 @@ type Config struct { SubRules map[string][]C.Rule Users []auth.AuthUser Proxies map[string]C.Proxy - Listeners map[string]C.NewListener + Listeners map[string]C.InboundListener Providers map[string]providerTypes.ProxyProvider RuleProviders map[string]providerTypes.RuleProvider Tunnels []tunnel.Tunnel @@ -694,8 +694,8 @@ func parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[ return proxies, providersMap, nil } -func parseListeners(cfg *RawConfig) (listeners map[string]C.NewListener, err error) { - listeners = make(map[string]C.NewListener) +func parseListeners(cfg *RawConfig) (listeners map[string]C.InboundListener, err error) { + listeners = make(map[string]C.InboundListener) for index, mapping := range cfg.Listeners { listener, err := L.ParseListener(mapping) if err != nil { diff --git a/constant/listener.go b/constant/listener.go index a6696af4..09ecdb76 100644 --- a/constant/listener.go +++ b/constant/listener.go @@ -14,11 +14,16 @@ type AdvanceListener interface { HandleConn(conn net.Conn, in chan<- ConnContext) } -type NewListener interface { +type InboundListener interface { Name() string Listen(tcpIn chan<- ConnContext, udpIn chan<- PacketAdapter) error Close() error Address() string RawAddress() string - Config() string + Config() InboundConfig +} + +type InboundConfig interface { + Name() string + Equal(config InboundConfig) bool } diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 94db8c0f..32737c02 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -135,31 +135,11 @@ func GetGeneral() *config.General { return general } -func updateListeners(listeners map[string]C.NewListener) { +func updateListeners(listeners map[string]C.InboundListener) { tcpIn := tunnel.TCPIn() udpIn := tunnel.UDPIn() - skipNames := map[string]struct{}{} - for name, oldListener := range tunnel.Listeners() { - if newListener, ok := listeners[name]; ok { - if newListener.Config() == oldListener.Config() { - listeners[name] = oldListener - skipNames[name] = struct{}{} - continue - } - } - _ = oldListener.Close() - } - - for name, newListener := range listeners { - if _, ok := skipNames[name]; ok { - continue - } - if err := newListener.Listen(tcpIn, udpIn); err != nil { - log.Errorln("Listener %s listen err: %s", newListener.Name(), err.Error()) - } - } - tunnel.UpdateListeners(listeners) + listener.PatchInboundListeners(listeners, tcpIn, udpIn, true) } func updateExperimental(c *config.Config) { diff --git a/listener/inbound/base.go b/listener/inbound/base.go index ebe65309..eee3cdce 100644 --- a/listener/inbound/base.go +++ b/listener/inbound/base.go @@ -26,7 +26,7 @@ func NewBase(options *BaseOption) (*Base, error) { return nil, err } return &Base{ - name: options.Name, + name: options.Name(), listenAddr: addr, preferRulesName: options.PreferRulesName, port: options.Port, @@ -34,44 +34,54 @@ func NewBase(options *BaseOption) (*Base, error) { }, nil } -// Config implements constant.NewListener -func (b *Base) Config() string { - return optionToString(b.config) +// Config implements constant.InboundListener +func (b *Base) Config() C.InboundConfig { + return b.config } -// Address implements constant.NewListener +// Address implements constant.InboundListener func (b *Base) Address() string { return b.RawAddress() } -// Close implements constant.NewListener +// Close implements constant.InboundListener func (*Base) Close() error { return nil } -// Name implements constant.NewListener +// Name implements constant.InboundListener func (b *Base) Name() string { return b.name } -// RawAddress implements constant.NewListener +// RawAddress implements constant.InboundListener func (b *Base) RawAddress() string { return net.JoinHostPort(b.listenAddr.String(), strconv.Itoa(int(b.port))) } -// Listen implements constant.NewListener +// Listen implements constant.InboundListener func (*Base) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { return nil } +var _ C.InboundListener = (*Base)(nil) + type BaseOption struct { - Name string `inbound:"name"` + NameStr string `inbound:"name"` Listen string `inbound:"listen,omitempty"` Port int `inbound:"port"` PreferRulesName string `inbound:"rule,omitempty"` } -var _ C.NewListener = (*Base)(nil) +func (o BaseOption) Name() string { + return o.NameStr +} + +func (o BaseOption) Equal(config C.InboundConfig) bool { + return optionToString(o) == optionToString(config) +} + +var _ C.InboundConfig = (*BaseOption)(nil) func optionToString(option any) string { str, _ := json.Marshal(option) diff --git a/listener/inbound/http.go b/listener/inbound/http.go index 6f04b4b6..cf373ed7 100644 --- a/listener/inbound/http.go +++ b/listener/inbound/http.go @@ -9,6 +9,11 @@ import ( type HTTPOption struct { BaseOption } + +func (o HTTPOption) Equal(config C.InboundConfig) bool { + return optionToString(o) == optionToString(config) +} + type HTTP struct { *Base config *HTTPOption @@ -26,17 +31,17 @@ func NewHTTP(options *HTTPOption) (*HTTP, error) { }, nil } -// Config implements constant.NewListener -func (h *HTTP) Config() string { - return optionToString(h.config) +// Config implements constant.InboundListener +func (h *HTTP) Config() C.InboundConfig { + return h.config } -// Address implements constant.NewListener +// Address implements constant.InboundListener func (h *HTTP) Address() string { return h.l.Address() } -// Listen implements constant.NewListener +// Listen implements constant.InboundListener func (h *HTTP) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error h.l, err = http.NewWithInfos(h.RawAddress(), h.name, h.preferRulesName, tcpIn) @@ -47,7 +52,7 @@ func (h *HTTP) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) return nil } -// Close implements constant.NewListener +// Close implements constant.InboundListener func (h *HTTP) Close() error { if h.l != nil { return h.l.Close() @@ -55,4 +60,4 @@ func (h *HTTP) Close() error { return nil } -var _ C.NewListener = (*HTTP)(nil) +var _ C.InboundListener = (*HTTP)(nil) diff --git a/listener/inbound/mixed.go b/listener/inbound/mixed.go index 01120dd7..222de4c5 100644 --- a/listener/inbound/mixed.go +++ b/listener/inbound/mixed.go @@ -12,7 +12,11 @@ import ( type MixedOption struct { BaseOption - UDP *bool `inbound:"udp,omitempty"` + UDP bool `inbound:"udp,omitempty"` +} + +func (o MixedOption) Equal(config C.InboundConfig) bool { + return optionToString(o) == optionToString(config) } type Mixed struct { @@ -31,21 +35,21 @@ func NewMixed(options *MixedOption) (*Mixed, error) { return &Mixed{ Base: base, config: options, - udp: options.UDP == nil || *options.UDP, + udp: options.UDP, }, nil } -// Config implements constant.NewListener -func (m *Mixed) Config() string { - return optionToString(m.config) +// Config implements constant.InboundListener +func (m *Mixed) Config() C.InboundConfig { + return m.config } -// Address implements constant.NewListener +// Address implements constant.InboundListener func (m *Mixed) Address() string { return m.l.Address() } -// Listen implements constant.NewListener +// Listen implements constant.InboundListener func (m *Mixed) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error m.l, err = mixed.NewWithInfos(m.RawAddress(), m.name, m.preferRulesName, tcpIn) @@ -62,7 +66,7 @@ func (m *Mixed) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) return nil } -// Close implements constant.NewListener +// Close implements constant.InboundListener func (m *Mixed) Close() error { var err error if m.l != nil { @@ -82,4 +86,4 @@ func (m *Mixed) Close() error { return err } -var _ C.NewListener = (*Mixed)(nil) +var _ C.InboundListener = (*Mixed)(nil) diff --git a/listener/inbound/redir.go b/listener/inbound/redir.go index 6d6ca4f0..1809c3bc 100644 --- a/listener/inbound/redir.go +++ b/listener/inbound/redir.go @@ -10,6 +10,10 @@ type RedirOption struct { BaseOption } +func (o RedirOption) Equal(config C.InboundConfig) bool { + return optionToString(o) == optionToString(config) +} + type Redir struct { *Base config *RedirOption @@ -27,17 +31,17 @@ func NewRedir(options *RedirOption) (*Redir, error) { }, nil } -// Config implements constant.NewListener -func (r *Redir) Config() string { - return optionToString(r.config) +// Config implements constant.InboundListener +func (r *Redir) Config() C.InboundConfig { + return r.config } -// Address implements constant.NewListener +// Address implements constant.InboundListener func (r *Redir) Address() string { return r.l.Address() } -// Listen implements constant.NewListener +// Listen implements constant.InboundListener func (r *Redir) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error r.l, err = redir.NewWithInfos(r.Address(), r.name, r.preferRulesName, tcpIn) @@ -48,7 +52,7 @@ func (r *Redir) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) return nil } -// Close implements constant.NewListener +// Close implements constant.InboundListener func (r *Redir) Close() error { if r.l != nil { r.l.Close() @@ -56,4 +60,4 @@ func (r *Redir) Close() error { return nil } -var _ C.NewListener = (*Redir)(nil) +var _ C.InboundListener = (*Redir)(nil) diff --git a/listener/inbound/socks.go b/listener/inbound/socks.go index 9e826bc3..10df3568 100644 --- a/listener/inbound/socks.go +++ b/listener/inbound/socks.go @@ -9,7 +9,11 @@ import ( type SocksOption struct { BaseOption - UDP *bool `inbound:"udp,omitempty"` + UDP bool `inbound:"udp,omitempty"` +} + +func (o SocksOption) Equal(config C.InboundConfig) bool { + return optionToString(o) == optionToString(config) } type Socks struct { @@ -28,16 +32,16 @@ func NewSocks(options *SocksOption) (*Socks, error) { return &Socks{ Base: base, config: options, - udp: options.UDP == nil || *options.UDP, + udp: options.UDP, }, nil } -// Config implements constant.NewListener -func (s *Socks) Config() string { - return optionToString(s.config) +// Config implements constant.InboundListener +func (s *Socks) Config() C.InboundConfig { + return s.config } -// Close implements constant.NewListener +// Close implements constant.InboundListener func (s *Socks) Close() error { var err error if s.stl != nil { @@ -58,12 +62,12 @@ func (s *Socks) Close() error { return err } -// Address implements constant.NewListener +// Address implements constant.InboundListener func (s *Socks) Address() string { return s.stl.Address() } -// Listen implements constant.NewListener +// Listen implements constant.InboundListener func (s *Socks) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error if s.stl, err = socks.NewWithInfos(s.RawAddress(), s.name, s.preferRulesName, tcpIn); err != nil { @@ -79,4 +83,4 @@ func (s *Socks) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) return nil } -var _ C.NewListener = (*Socks)(nil) +var _ C.InboundListener = (*Socks)(nil) diff --git a/listener/inbound/tproxy.go b/listener/inbound/tproxy.go index 3a86919c..ff5893be 100644 --- a/listener/inbound/tproxy.go +++ b/listener/inbound/tproxy.go @@ -10,7 +10,11 @@ import ( type TProxyOption struct { BaseOption - UDP *bool `inbound:"udp,omitempty"` + UDP bool `inbound:"udp,omitempty"` +} + +func (o TProxyOption) Equal(config C.InboundConfig) bool { + return optionToString(o) == optionToString(config) } type TProxy struct { @@ -29,22 +33,22 @@ func NewTProxy(options *TProxyOption) (*TProxy, error) { return &TProxy{ Base: base, config: options, - udp: options.UDP == nil || *options.UDP, + udp: options.UDP, }, nil } -// Config implements constant.NewListener -func (t *TProxy) Config() string { - return optionToString(t.config) +// Config implements constant.InboundListener +func (t *TProxy) Config() C.InboundConfig { + return t.config } -// Address implements constant.NewListener +// Address implements constant.InboundListener func (t *TProxy) Address() string { return t.lTCP.Address() } -// Listen implements constant.NewListener +// Listen implements constant.InboundListener func (t *TProxy) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error t.lTCP, err = tproxy.NewWithInfos(t.RawAddress(), t.name, t.preferRulesName, tcpIn) @@ -64,7 +68,7 @@ func (t *TProxy) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter return nil } -// Close implements constant.NewListener +// Close implements constant.InboundListener func (t *TProxy) Close() error { var tcpErr error var udpErr error @@ -87,4 +91,4 @@ func (t *TProxy) Close() error { return nil } -var _ C.NewListener = (*TProxy)(nil) +var _ C.InboundListener = (*TProxy)(nil) diff --git a/listener/listener.go b/listener/listener.go index 836bfb2a..4c976b31 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -43,6 +43,7 @@ var ( mixedUDPLister *socks.UDPListener tunnelTCPListeners = map[string]*tunnel.Listener{} tunnelUDPListeners = map[string]*tunnel.PacketConn{} + inboundListeners = map[string]C.InboundListener{} tunLister *sing_tun.Listener shadowSocksListener C.AdvanceListener vmessListener *sing_vmess.Listener @@ -58,6 +59,7 @@ var ( tproxyMux sync.Mutex mixedMux sync.Mutex tunnelMux sync.Mutex + inboundMux sync.Mutex tunMux sync.Mutex ssMux sync.Mutex vmessMux sync.Mutex @@ -682,6 +684,35 @@ func PatchTunnel(tunnels []tunnel.Tunnel, tcpIn chan<- C.ConnContext, udpIn chan } } +func PatchInboundListeners(newListenerMap map[string]C.InboundListener, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, dropOld bool) { + inboundMux.Lock() + defer inboundMux.Unlock() + + for name, newListener := range newListenerMap { + if oldListener, ok := inboundListeners[name]; ok { + if !oldListener.Config().Equal(newListener.Config()) { + _ = oldListener.Close() + } else { + continue + } + } + if err := newListener.Listen(tcpIn, udpIn); err != nil { + log.Errorln("Listener %s listen err: %s", name, err.Error()) + continue + } + inboundListeners[name] = newListener + } + + if dropOld { + for name, oldListener := range inboundListeners { + if _, ok := newListenerMap[name]; !ok { + _ = oldListener.Close() + delete(inboundListeners, name) + } + } + } +} + // GetPorts return the ports of proxy servers func GetPorts() *Ports { ports := &Ports{} diff --git a/listener/parse.go b/listener/parse.go index ee85e26c..04573c71 100644 --- a/listener/parse.go +++ b/listener/parse.go @@ -2,29 +2,26 @@ package listener import ( "fmt" - "strings" "github.com/Dreamacro/clash/common/structure" C "github.com/Dreamacro/clash/constant" IN "github.com/Dreamacro/clash/listener/inbound" ) -var keyReplacer = strings.NewReplacer("_", "-") - -func ParseListener(mapping map[string]any) (C.NewListener, error) { - decoder := structure.NewDecoder(structure.Option{TagName: "inbound", WeaklyTypedInput: true, KeyReplacer: keyReplacer}) +func ParseListener(mapping map[string]any) (C.InboundListener, error) { + decoder := structure.NewDecoder(structure.Option{TagName: "inbound", WeaklyTypedInput: true, KeyReplacer: structure.DefaultKeyReplacer}) proxyType, existType := mapping["type"].(string) if !existType { return nil, fmt.Errorf("missing type") } var ( - listener C.NewListener + listener C.InboundListener err error ) switch proxyType { case "socks": - socksOption := &IN.SocksOption{} + socksOption := &IN.SocksOption{UDP: true} err = decoder.Decode(mapping, socksOption) if err != nil { return nil, err @@ -38,7 +35,7 @@ func ParseListener(mapping map[string]any) (C.NewListener, error) { } listener, err = IN.NewHTTP(httpOption) case "tproxy": - tproxyOption := &IN.TProxyOption{} + tproxyOption := &IN.TProxyOption{UDP: true} err = decoder.Decode(mapping, tproxyOption) if err != nil { return nil, err @@ -52,7 +49,7 @@ func ParseListener(mapping map[string]any) (C.NewListener, error) { } listener, err = IN.NewRedir(redirOption) case "mixed": - mixedOption := &IN.MixedOption{} + mixedOption := &IN.MixedOption{UDP: true} err = decoder.Decode(mapping, mixedOption) if err != nil { return nil, err diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 3be2f4aa..943bd917 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -29,7 +29,7 @@ var ( udpQueue = make(chan C.PacketAdapter, 200) natTable = nat.New() rules []C.Rule - listeners = make(map[string]C.NewListener) + listeners = make(map[string]C.InboundListener) subRules map[string][]C.Rule proxies = make(map[string]C.Proxy) providers map[string]provider.ProxyProvider @@ -87,7 +87,7 @@ func Rules() []C.Rule { return rules } -func Listeners() map[string]C.NewListener { +func Listeners() map[string]C.InboundListener { return listeners } @@ -123,7 +123,7 @@ func UpdateProxies(newProxies map[string]C.Proxy, newProviders map[string]provid configMux.Unlock() } -func UpdateListeners(newListeners map[string]C.NewListener) { +func UpdateListeners(newListeners map[string]C.InboundListener) { configMux.Lock() defer configMux.Unlock() listeners = newListeners From 62474e0ed6e6a3898d1e2e4bf095362b300a1b27 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 4 Dec 2022 22:08:20 +0800 Subject: [PATCH 196/250] fix: correct C.Metadata --- adapter/inbound/http.go | 4 ++-- adapter/inbound/https.go | 4 ++-- adapter/inbound/packet.go | 4 ++-- adapter/inbound/socket.go | 4 ++-- constant/metadata.go | 34 +++++++++++++++++----------------- listener/autoredir/tcp.go | 26 +++++++++++++------------- listener/http/client.go | 4 ++-- listener/http/proxy.go | 8 ++++---- listener/http/server.go | 26 +++++++++++++------------- listener/http/upgrade.go | 4 ++-- listener/inbound/base.go | 20 ++++++++++---------- listener/inbound/http.go | 2 +- listener/inbound/mixed.go | 4 ++-- listener/inbound/redir.go | 2 +- listener/inbound/socks.go | 4 ++-- listener/inbound/tproxy.go | 4 ++-- listener/mixed/mixed.go | 34 +++++++++++++++++----------------- listener/redir/tcp.go | 26 +++++++++++++------------- listener/socks/tcp.go | 36 ++++++++++++++++++------------------ listener/socks/udp.go | 26 +++++++++++++------------- listener/tproxy/tproxy.go | 26 +++++++++++++------------- listener/tproxy/udp.go | 18 +++++++++--------- tunnel/tunnel.go | 2 +- 23 files changed, 161 insertions(+), 161 deletions(-) diff --git a/adapter/inbound/http.go b/adapter/inbound/http.go index 4557d470..0637b39a 100644 --- a/adapter/inbound/http.go +++ b/adapter/inbound/http.go @@ -13,12 +13,12 @@ func NewHTTP(target socks5.Addr, source net.Addr, conn net.Conn) *context.ConnCo return NewHTTPWithInfos(target, source, conn, "", "") } -func NewHTTPWithInfos(target socks5.Addr, source net.Addr, conn net.Conn, inName, preferRulesName string) *context.ConnContext { +func NewHTTPWithInfos(target socks5.Addr, source net.Addr, conn net.Conn, inName, specialRules string) *context.ConnContext { metadata := parseSocksAddr(target) metadata.NetWork = C.TCP metadata.Type = C.HTTP metadata.InName = inName - metadata.PreferRulesName = preferRulesName + metadata.SpecialRules = specialRules if ip, port, err := parseAddr(source.String()); err == nil { metadata.SrcIP = ip metadata.SrcPort = port diff --git a/adapter/inbound/https.go b/adapter/inbound/https.go index 882802ef..dbc17835 100644 --- a/adapter/inbound/https.go +++ b/adapter/inbound/https.go @@ -13,10 +13,10 @@ func NewHTTPS(request *http.Request, conn net.Conn) *context.ConnContext { return NewHTTPSWithInfos(request, conn, "", "") } -func NewHTTPSWithInfos(request *http.Request, conn net.Conn, inName, preferRulesName string) *context.ConnContext { +func NewHTTPSWithInfos(request *http.Request, conn net.Conn, inName, specialRules string) *context.ConnContext { metadata := parseHTTPAddr(request) metadata.Type = C.HTTPS - metadata.PreferRulesName = preferRulesName + metadata.SpecialRules = specialRules metadata.InName = inName if ip, port, err := parseAddr(conn.RemoteAddr().String()); err == nil { metadata.SrcIP = ip diff --git a/adapter/inbound/packet.go b/adapter/inbound/packet.go index 524c8f52..a84a3008 100644 --- a/adapter/inbound/packet.go +++ b/adapter/inbound/packet.go @@ -16,12 +16,12 @@ func (s *PacketAdapter) Metadata() *C.Metadata { return s.metadata } -func NewPacketWithInfos(target socks5.Addr, packet C.UDPPacket, source C.Type, inName, preferRulesName string) C.PacketAdapter { +func NewPacketWithInfos(target socks5.Addr, packet C.UDPPacket, source C.Type, inName, specialRules string) C.PacketAdapter { metadata := parseSocksAddr(target) metadata.NetWork = C.UDP metadata.Type = source metadata.InName = inName - metadata.PreferRulesName = preferRulesName + metadata.SpecialRules = specialRules if ip, port, err := parseAddr(packet.LocalAddr().String()); err == nil { metadata.SrcIP = ip metadata.SrcPort = port diff --git a/adapter/inbound/socket.go b/adapter/inbound/socket.go index 6bcdbced..34f31cf3 100644 --- a/adapter/inbound/socket.go +++ b/adapter/inbound/socket.go @@ -9,11 +9,11 @@ import ( "github.com/Dreamacro/clash/transport/socks5" ) -func NewSocketWithInfos(target socks5.Addr, conn net.Conn, source C.Type, inName , preferRulesName string) *context.ConnContext { +func NewSocketWithInfos(target socks5.Addr, conn net.Conn, source C.Type, inName, specialRules string) *context.ConnContext { metadata := parseSocksAddr(target) metadata.NetWork = C.TCP metadata.Type = source - metadata.PreferRulesName = preferRulesName + metadata.SpecialRules = specialRules metadata.InName = inName remoteAddr := conn.RemoteAddr() diff --git a/constant/metadata.go b/constant/metadata.go index 0102ba74..278304bd 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -117,23 +117,23 @@ func (t Type) MarshalJSON() ([]byte, error) { // Metadata is used to store connection address type Metadata struct { - NetWork NetWork `json:"network"` - Type Type `json:"type"` - SrcIP netip.Addr `json:"sourceIP"` - DstIP netip.Addr `json:"destinationIP"` - SrcPort string `json:"sourcePort"` - DstPort string `json:"destinationPort"` - InIP netip.Addr `json:"inboundIP"` - InPort string `json:"inboundPort"` - Host string `json:"host"` - DNSMode DNSMode `json:"dnsMode"` - Uid *uint32 `json:"uid"` - Process string `json:"process"` - ProcessPath string `json:"processPath"` - SpecialProxy string `json:"specialProxy"` - RemoteDst string `json:"remoteDestination"` - InName string `jsson:"-"` - PreferRulesName string + NetWork NetWork `json:"network"` + Type Type `json:"type"` + SrcIP netip.Addr `json:"sourceIP"` + DstIP netip.Addr `json:"destinationIP"` + SrcPort string `json:"sourcePort"` + DstPort string `json:"destinationPort"` + InIP netip.Addr `json:"inboundIP"` + InPort string `json:"inboundPort"` + Host string `json:"host"` + DNSMode DNSMode `json:"dnsMode"` + Uid *uint32 `json:"uid"` + Process string `json:"process"` + ProcessPath string `json:"processPath"` + SpecialProxy string `json:"specialProxy"` + RemoteDst string `json:"remoteDestination"` + InName string `json:"inboundName"` + SpecialRules string `json:"specialRules"` } func (m *Metadata) RemoteAddress() string { diff --git a/listener/autoredir/tcp.go b/listener/autoredir/tcp.go index 936bffb5..5e675540 100644 --- a/listener/autoredir/tcp.go +++ b/listener/autoredir/tcp.go @@ -11,12 +11,12 @@ import ( ) type Listener struct { - listener net.Listener - addr string - closed bool - name string - preferRulesName string - lookupFunc func(netip.AddrPort) (socks5.Addr, error) + listener net.Listener + addr string + closed bool + name string + specialRules string + lookupFunc func(netip.AddrPort) (socks5.Addr, error) } // RawAddress implements C.Listener @@ -58,23 +58,23 @@ func (l *Listener) handleRedir(conn net.Conn, in chan<- C.ConnContext) { _ = conn.(*net.TCPConn).SetKeepAlive(true) - in <- inbound.NewSocketWithInfos(target, conn, C.REDIR,l.name,l.preferRulesName) + in <- inbound.NewSocketWithInfos(target, conn, C.REDIR, l.name, l.specialRules) } func New(addr string, in chan<- C.ConnContext) (*Listener, error) { - return NewWithInfos(addr,"DEFAULT-REDIR","",in) + return NewWithInfos(addr, "DEFAULT-REDIR", "", in) } -func NewWithInfos(addr ,name,preferRulesName string, in chan<- C.ConnContext) (*Listener, error) { +func NewWithInfos(addr, name, specialRules string, in chan<- C.ConnContext) (*Listener, error) { l, err := net.Listen("tcp", addr) if err != nil { return nil, err } rl := &Listener{ - listener: l, - addr: addr, - name:name, - preferRulesName: preferRulesName, + listener: l, + addr: addr, + name: name, + specialRules: specialRules, } go func() { diff --git a/listener/http/client.go b/listener/http/client.go index 8b21f98b..64208e17 100644 --- a/listener/http/client.go +++ b/listener/http/client.go @@ -12,7 +12,7 @@ import ( "github.com/Dreamacro/clash/transport/socks5" ) -func newClient(source net.Addr,name,preferRulesName string, in chan<- C.ConnContext) *http.Client { +func newClient(source net.Addr, name, specialRules string, in chan<- C.ConnContext) *http.Client { return &http.Client{ Transport: &http.Transport{ // from http.DefaultTransport @@ -32,7 +32,7 @@ func newClient(source net.Addr,name,preferRulesName string, in chan<- C.ConnCont left, right := net.Pipe() - in <- inbound.NewHTTPWithInfos(dstAddr, source, right,name,preferRulesName) + in <- inbound.NewHTTPWithInfos(dstAddr, source, right, name, specialRules) return left, nil }, diff --git a/listener/http/proxy.go b/listener/http/proxy.go index 74d9d77e..afded98e 100644 --- a/listener/http/proxy.go +++ b/listener/http/proxy.go @@ -14,8 +14,8 @@ import ( "github.com/Dreamacro/clash/log" ) -func HandleConn(name, preferRulesName string, c net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[string, bool]) { - client := newClient(c.RemoteAddr(), name, preferRulesName, in) +func HandleConn(name, specialRules string, c net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[string, bool]) { + client := newClient(c.RemoteAddr(), name, specialRules, in) defer client.CloseIdleConnections() conn := N.NewBufferedConn(c) @@ -48,7 +48,7 @@ func HandleConn(name, preferRulesName string, c net.Conn, in chan<- C.ConnContex break // close connection } - in <- inbound.NewHTTPSWithInfos(request, conn, name, preferRulesName) + in <- inbound.NewHTTPSWithInfos(request, conn, name, specialRules) return // hijack connection } @@ -61,7 +61,7 @@ func HandleConn(name, preferRulesName string, c net.Conn, in chan<- C.ConnContex request.RequestURI = "" if isUpgradeRequest(request) { - handleUpgrade(name, preferRulesName, conn, request, in) + handleUpgrade(name, specialRules, conn, request, in) return // hijack connection } diff --git a/listener/http/server.go b/listener/http/server.go index 7bcc542f..fa51ba28 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -9,11 +9,11 @@ import ( ) type Listener struct { - listener net.Listener - addr string - closed bool - name string - preferRulesName string + listener net.Listener + addr string + closed bool + name string + specialRules string } // RawAddress implements C.Listener @@ -36,11 +36,11 @@ func New(addr string, in chan<- C.ConnContext) (*Listener, error) { return NewWithAuthenticate(addr, "DEFAULT-HTTP", "", in, true) } -func NewWithInfos(addr, name, preferRulesName string, in chan<- C.ConnContext) (*Listener, error) { - return NewWithAuthenticate(addr, name, preferRulesName, in, true) +func NewWithInfos(addr, name, specialRules string, in chan<- C.ConnContext) (*Listener, error) { + return NewWithAuthenticate(addr, name, specialRules, in, true) } -func NewWithAuthenticate(addr, name, preferRulesName string, in chan<- C.ConnContext, authenticate bool) (*Listener, error) { +func NewWithAuthenticate(addr, name, specialRules string, in chan<- C.ConnContext, authenticate bool) (*Listener, error) { l, err := inbound.Listen("tcp", addr) if err != nil { @@ -53,10 +53,10 @@ func NewWithAuthenticate(addr, name, preferRulesName string, in chan<- C.ConnCon } hl := &Listener{ - listener: l, - name: name, - preferRulesName: preferRulesName, - addr: addr, + listener: l, + name: name, + specialRules: specialRules, + addr: addr, } go func() { for { @@ -67,7 +67,7 @@ func NewWithAuthenticate(addr, name, preferRulesName string, in chan<- C.ConnCon } continue } - go HandleConn(hl.name, hl.preferRulesName, conn, in, c) + go HandleConn(hl.name, hl.specialRules, conn, in, c) } }() diff --git a/listener/http/upgrade.go b/listener/http/upgrade.go index b0f1e389..a4601995 100644 --- a/listener/http/upgrade.go +++ b/listener/http/upgrade.go @@ -25,7 +25,7 @@ func isUpgradeRequest(req *http.Request) bool { return false } -func handleUpgrade(name,preferRulesName string,conn net.Conn, request *http.Request, in chan<- C.ConnContext) { +func handleUpgrade(name, specialRules string, conn net.Conn, request *http.Request, in chan<- C.ConnContext) { defer conn.Close() removeProxyHeaders(request.Header) @@ -43,7 +43,7 @@ func handleUpgrade(name,preferRulesName string,conn net.Conn, request *http.Requ left, right := net.Pipe() - in <- inbound.NewHTTPWithInfos(dstAddr, conn.RemoteAddr(), right,name,preferRulesName) + in <- inbound.NewHTTPWithInfos(dstAddr, conn.RemoteAddr(), right, name, specialRules) var bufferedLeft *N.BufferedConn if request.TLS != nil { diff --git a/listener/inbound/base.go b/listener/inbound/base.go index eee3cdce..7428e716 100644 --- a/listener/inbound/base.go +++ b/listener/inbound/base.go @@ -10,11 +10,11 @@ import ( ) type Base struct { - config *BaseOption - name string - preferRulesName string - listenAddr netip.Addr - port int + config *BaseOption + name string + specialRules string + listenAddr netip.Addr + port int } func NewBase(options *BaseOption) (*Base, error) { @@ -26,11 +26,11 @@ func NewBase(options *BaseOption) (*Base, error) { return nil, err } return &Base{ - name: options.Name(), - listenAddr: addr, - preferRulesName: options.PreferRulesName, - port: options.Port, - config: options, + name: options.Name(), + listenAddr: addr, + specialRules: options.PreferRulesName, + port: options.Port, + config: options, }, nil } diff --git a/listener/inbound/http.go b/listener/inbound/http.go index cf373ed7..b6edb479 100644 --- a/listener/inbound/http.go +++ b/listener/inbound/http.go @@ -44,7 +44,7 @@ func (h *HTTP) Address() string { // Listen implements constant.InboundListener func (h *HTTP) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error - h.l, err = http.NewWithInfos(h.RawAddress(), h.name, h.preferRulesName, tcpIn) + h.l, err = http.NewWithInfos(h.RawAddress(), h.name, h.specialRules, tcpIn) if err != nil { return err } diff --git a/listener/inbound/mixed.go b/listener/inbound/mixed.go index 222de4c5..f29caebb 100644 --- a/listener/inbound/mixed.go +++ b/listener/inbound/mixed.go @@ -52,12 +52,12 @@ func (m *Mixed) Address() string { // Listen implements constant.InboundListener func (m *Mixed) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error - m.l, err = mixed.NewWithInfos(m.RawAddress(), m.name, m.preferRulesName, tcpIn) + m.l, err = mixed.NewWithInfos(m.RawAddress(), m.name, m.specialRules, tcpIn) if err != nil { return err } if m.udp { - m.lUDP, err = socks.NewUDPWithInfos(m.Address(), m.name, m.preferRulesName, udpIn) + m.lUDP, err = socks.NewUDPWithInfos(m.Address(), m.name, m.specialRules, udpIn) if err != nil { return err } diff --git a/listener/inbound/redir.go b/listener/inbound/redir.go index 1809c3bc..9f0f35b4 100644 --- a/listener/inbound/redir.go +++ b/listener/inbound/redir.go @@ -44,7 +44,7 @@ func (r *Redir) Address() string { // Listen implements constant.InboundListener func (r *Redir) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error - r.l, err = redir.NewWithInfos(r.Address(), r.name, r.preferRulesName, tcpIn) + r.l, err = redir.NewWithInfos(r.Address(), r.name, r.specialRules, tcpIn) if err != nil { return err } diff --git a/listener/inbound/socks.go b/listener/inbound/socks.go index 10df3568..9727350b 100644 --- a/listener/inbound/socks.go +++ b/listener/inbound/socks.go @@ -70,11 +70,11 @@ func (s *Socks) Address() string { // Listen implements constant.InboundListener func (s *Socks) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error - if s.stl, err = socks.NewWithInfos(s.RawAddress(), s.name, s.preferRulesName, tcpIn); err != nil { + if s.stl, err = socks.NewWithInfos(s.RawAddress(), s.name, s.specialRules, tcpIn); err != nil { return err } if s.udp { - if s.sul, err = socks.NewUDPWithInfos(s.RawAddress(), s.name, s.preferRulesName, udpIn); err != nil { + if s.sul, err = socks.NewUDPWithInfos(s.RawAddress(), s.name, s.specialRules, udpIn); err != nil { return err } } diff --git a/listener/inbound/tproxy.go b/listener/inbound/tproxy.go index ff5893be..c3e34033 100644 --- a/listener/inbound/tproxy.go +++ b/listener/inbound/tproxy.go @@ -51,13 +51,13 @@ func (t *TProxy) Address() string { // Listen implements constant.InboundListener func (t *TProxy) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error - t.lTCP, err = tproxy.NewWithInfos(t.RawAddress(), t.name, t.preferRulesName, tcpIn) + t.lTCP, err = tproxy.NewWithInfos(t.RawAddress(), t.name, t.specialRules, tcpIn) if err != nil { return err } if t.udp { if t.lUDP != nil { - t.lUDP, err = tproxy.NewUDPWithInfos(t.Address(), t.name, t.preferRulesName, udpIn) + t.lUDP, err = tproxy.NewUDPWithInfos(t.Address(), t.name, t.specialRules, udpIn) if err != nil { return err } diff --git a/listener/mixed/mixed.go b/listener/mixed/mixed.go index fe42a14e..34b4b1af 100644 --- a/listener/mixed/mixed.go +++ b/listener/mixed/mixed.go @@ -14,12 +14,12 @@ import ( ) type Listener struct { - listener net.Listener - addr string - name string - preferRulesName string - cache *cache.LruCache[string, bool] - closed bool + listener net.Listener + addr string + name string + specialRules string + cache *cache.LruCache[string, bool] + closed bool } // RawAddress implements C.Listener @@ -42,18 +42,18 @@ func New(addr string, in chan<- C.ConnContext) (*Listener, error) { return NewWithInfos(addr, "DEFAULT-MIXED", "", in) } -func NewWithInfos(addr, name, preferRulesName string, in chan<- C.ConnContext) (*Listener, error) { +func NewWithInfos(addr, name, specialRules string, in chan<- C.ConnContext) (*Listener, error) { l, err := inbound.Listen("tcp", addr) if err != nil { return nil, err } ml := &Listener{ - listener: l, - addr: addr, - name: name, - preferRulesName: preferRulesName, - cache: cache.New[string, bool](cache.WithAge[string, bool](30)), + listener: l, + addr: addr, + name: name, + specialRules: specialRules, + cache: cache.New[string, bool](cache.WithAge[string, bool](30)), } go func() { for { @@ -64,14 +64,14 @@ func NewWithInfos(addr, name, preferRulesName string, in chan<- C.ConnContext) ( } continue } - go handleConn(ml.name, ml.preferRulesName, c, in, ml.cache) + go handleConn(ml.name, ml.specialRules, c, in, ml.cache) } }() return ml, nil } -func handleConn(name, preferRulesName string, conn net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[string, bool]) { +func handleConn(name, specialRules string, conn net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[string, bool]) { conn.(*net.TCPConn).SetKeepAlive(true) bufConn := N.NewBufferedConn(conn) @@ -82,10 +82,10 @@ func handleConn(name, preferRulesName string, conn net.Conn, in chan<- C.ConnCon switch head[0] { case socks4.Version: - socks.HandleSocks4(name, preferRulesName, bufConn, in) + socks.HandleSocks4(name, specialRules, bufConn, in) case socks5.Version: - socks.HandleSocks5(name, preferRulesName, bufConn, in) + socks.HandleSocks5(name, specialRules, bufConn, in) default: - http.HandleConn(name, preferRulesName, bufConn, in, cache) + http.HandleConn(name, specialRules, bufConn, in, cache) } } diff --git a/listener/redir/tcp.go b/listener/redir/tcp.go index a9e9b5b8..150f1da5 100644 --- a/listener/redir/tcp.go +++ b/listener/redir/tcp.go @@ -8,11 +8,11 @@ import ( ) type Listener struct { - listener net.Listener - addr string - closed bool - name string - preferRulesName string + listener net.Listener + addr string + closed bool + name string + specialRules string } // RawAddress implements C.Listener @@ -35,16 +35,16 @@ func New(addr string, in chan<- C.ConnContext) (*Listener, error) { return NewWithInfos(addr, "DEFAULT-REDIR", "", in) } -func NewWithInfos(addr, name, preferRulesName string, in chan<- C.ConnContext) (*Listener, error) { +func NewWithInfos(addr, name, specialRules string, in chan<- C.ConnContext) (*Listener, error) { l, err := net.Listen("tcp", addr) if err != nil { return nil, err } rl := &Listener{ - listener: l, - addr: addr, - name: name, - preferRulesName: preferRulesName, + listener: l, + addr: addr, + name: name, + specialRules: specialRules, } go func() { @@ -56,18 +56,18 @@ func NewWithInfos(addr, name, preferRulesName string, in chan<- C.ConnContext) ( } continue } - go handleRedir(rl.name, rl.preferRulesName, c, in) + go handleRedir(rl.name, rl.specialRules, c, in) } }() return rl, nil } -func handleRedir(name, preferRulesName string, conn net.Conn, in chan<- C.ConnContext) { +func handleRedir(name, specialRules string, conn net.Conn, in chan<- C.ConnContext) { target, err := parserPacket(conn) if err != nil { conn.Close() return } conn.(*net.TCPConn).SetKeepAlive(true) - in <- inbound.NewSocketWithInfos(target, conn, C.REDIR, name, preferRulesName) + in <- inbound.NewSocketWithInfos(target, conn, C.REDIR, name, specialRules) } diff --git a/listener/socks/tcp.go b/listener/socks/tcp.go index 30174b61..dc0aff8f 100644 --- a/listener/socks/tcp.go +++ b/listener/socks/tcp.go @@ -13,11 +13,11 @@ import ( ) type Listener struct { - listener net.Listener - addr string - closed bool - preferRulesName string - name string + listener net.Listener + addr string + closed bool + specialRules string + name string } // RawAddress implements C.Listener @@ -40,17 +40,17 @@ func New(addr string, in chan<- C.ConnContext) (*Listener, error) { return NewWithInfos(addr, "DEFAULT-SOCKS", "", in) } -func NewWithInfos(addr, name, preferRulesName string, in chan<- C.ConnContext) (*Listener, error) { +func NewWithInfos(addr, name, specialRules string, in chan<- C.ConnContext) (*Listener, error) { l, err := inbound.Listen("tcp", addr) if err != nil { return nil, err } sl := &Listener{ - listener: l, - addr: addr, - name: name, - preferRulesName: preferRulesName, + listener: l, + addr: addr, + name: name, + specialRules: specialRules, } go func() { for { @@ -61,14 +61,14 @@ func NewWithInfos(addr, name, preferRulesName string, in chan<- C.ConnContext) ( } continue } - go handleSocks(sl.name, sl.preferRulesName, c, in) + go handleSocks(sl.name, sl.specialRules, c, in) } }() return sl, nil } -func handleSocks(name, preferRulesName string, conn net.Conn, in chan<- C.ConnContext) { +func handleSocks(name, specialRules string, conn net.Conn, in chan<- C.ConnContext) { conn.(*net.TCPConn).SetKeepAlive(true) bufConn := N.NewBufferedConn(conn) head, err := bufConn.Peek(1) @@ -79,24 +79,24 @@ func handleSocks(name, preferRulesName string, conn net.Conn, in chan<- C.ConnCo switch head[0] { case socks4.Version: - HandleSocks4(name, preferRulesName, bufConn, in) + HandleSocks4(name, specialRules, bufConn, in) case socks5.Version: - HandleSocks5(name, preferRulesName, bufConn, in) + HandleSocks5(name, specialRules, bufConn, in) default: conn.Close() } } -func HandleSocks4(name, preferRulesName string, conn net.Conn, in chan<- C.ConnContext) { +func HandleSocks4(name, specialRules string, conn net.Conn, in chan<- C.ConnContext) { addr, _, err := socks4.ServerHandshake(conn, authStore.Authenticator()) if err != nil { conn.Close() return } - in <- inbound.NewSocketWithInfos(socks5.ParseAddr(addr), conn, C.SOCKS4, name, preferRulesName) + in <- inbound.NewSocketWithInfos(socks5.ParseAddr(addr), conn, C.SOCKS4, name, specialRules) } -func HandleSocks5(name, preferRulesName string, conn net.Conn, in chan<- C.ConnContext) { +func HandleSocks5(name, specialRules string, conn net.Conn, in chan<- C.ConnContext) { target, command, err := socks5.ServerHandshake(conn, authStore.Authenticator()) if err != nil { conn.Close() @@ -107,5 +107,5 @@ func HandleSocks5(name, preferRulesName string, conn net.Conn, in chan<- C.ConnC io.Copy(io.Discard, conn) return } - in <- inbound.NewSocketWithInfos(target, conn, C.SOCKS5, name, preferRulesName) + in <- inbound.NewSocketWithInfos(target, conn, C.SOCKS5, name, specialRules) } diff --git a/listener/socks/udp.go b/listener/socks/udp.go index cde92c54..c7232d9d 100644 --- a/listener/socks/udp.go +++ b/listener/socks/udp.go @@ -12,11 +12,11 @@ import ( ) type UDPListener struct { - packetConn net.PacketConn - addr string - closed bool - name string - preferRulesName string + packetConn net.PacketConn + addr string + closed bool + name string + specialRules string } // RawAddress implements C.Listener @@ -39,7 +39,7 @@ func NewUDP(addr string, in chan<- C.PacketAdapter) (*UDPListener, error) { return NewUDPWithInfos(addr, "DEFAULT-SOCKS", "", in) } -func NewUDPWithInfos(addr, name, preferRulesName string, in chan<- C.PacketAdapter) (*UDPListener, error) { +func NewUDPWithInfos(addr, name, specialRules string, in chan<- C.PacketAdapter) (*UDPListener, error) { l, err := net.ListenPacket("udp", addr) if err != nil { return nil, err @@ -50,10 +50,10 @@ func NewUDPWithInfos(addr, name, preferRulesName string, in chan<- C.PacketAdapt } sl := &UDPListener{ - packetConn: l, - addr: addr, - preferRulesName: preferRulesName, - name: name, + packetConn: l, + addr: addr, + specialRules: specialRules, + name: name, } go func() { for { @@ -66,14 +66,14 @@ func NewUDPWithInfos(addr, name, preferRulesName string, in chan<- C.PacketAdapt } continue } - handleSocksUDP(sl.name, sl.preferRulesName, l, in, buf[:n], remoteAddr) + handleSocksUDP(sl.name, sl.specialRules, l, in, buf[:n], remoteAddr) } }() return sl, nil } -func handleSocksUDP(name, preferRulesName string, pc net.PacketConn, in chan<- C.PacketAdapter, buf []byte, addr net.Addr) { +func handleSocksUDP(name, specialRules string, pc net.PacketConn, in chan<- C.PacketAdapter, buf []byte, addr net.Addr) { target, payload, err := socks5.DecodeUDPPacket(buf) if err != nil { // Unresolved UDP packet, return buffer to the pool @@ -87,7 +87,7 @@ func handleSocksUDP(name, preferRulesName string, pc net.PacketConn, in chan<- C bufRef: buf, } select { - case in <- inbound.NewPacketWithInfos(target, packet, C.SOCKS5, name, preferRulesName): + case in <- inbound.NewPacketWithInfos(target, packet, C.SOCKS5, name, specialRules): default: } } diff --git a/listener/tproxy/tproxy.go b/listener/tproxy/tproxy.go index a1c53b53..857eb5e0 100644 --- a/listener/tproxy/tproxy.go +++ b/listener/tproxy/tproxy.go @@ -9,11 +9,11 @@ import ( ) type Listener struct { - listener net.Listener - addr string - closed bool - name string - preferRulesName string + listener net.Listener + addr string + closed bool + name string + specialRules string } // RawAddress implements C.Listener @@ -32,17 +32,17 @@ func (l *Listener) Close() error { return l.listener.Close() } -func (l *Listener) handleTProxy(name, preferRulesName string, conn net.Conn, in chan<- C.ConnContext) { +func (l *Listener) handleTProxy(name, specialRules string, conn net.Conn, in chan<- C.ConnContext) { target := socks5.ParseAddrToSocksAddr(conn.LocalAddr()) conn.(*net.TCPConn).SetKeepAlive(true) - in <- inbound.NewSocketWithInfos(target, conn, C.TPROXY, name, preferRulesName) + in <- inbound.NewSocketWithInfos(target, conn, C.TPROXY, name, specialRules) } func New(addr string, in chan<- C.ConnContext) (*Listener, error) { return NewWithInfos(addr, "DEFAULT-TPROXY", "", in) } -func NewWithInfos(addr, name, preferRulesName string, in chan<- C.ConnContext) (*Listener, error) { +func NewWithInfos(addr, name, specialRules string, in chan<- C.ConnContext) (*Listener, error) { l, err := net.Listen("tcp", addr) if err != nil { return nil, err @@ -60,10 +60,10 @@ func NewWithInfos(addr, name, preferRulesName string, in chan<- C.ConnContext) ( } rl := &Listener{ - listener: l, - addr: addr, - name: name, - preferRulesName: preferRulesName, + listener: l, + addr: addr, + name: name, + specialRules: specialRules, } go func() { @@ -75,7 +75,7 @@ func NewWithInfos(addr, name, preferRulesName string, in chan<- C.ConnContext) ( } continue } - go rl.handleTProxy(rl.name, rl.preferRulesName, c, in) + go rl.handleTProxy(rl.name, rl.specialRules, c, in) } }() diff --git a/listener/tproxy/udp.go b/listener/tproxy/udp.go index 9560a626..c25bf103 100644 --- a/listener/tproxy/udp.go +++ b/listener/tproxy/udp.go @@ -11,11 +11,11 @@ import ( ) type UDPListener struct { - packetConn net.PacketConn - addr string - closed bool - name string - preferRulesName string + packetConn net.PacketConn + addr string + closed bool + name string + specialRules string } // RawAddress implements C.Listener @@ -38,7 +38,7 @@ func NewUDP(addr string, in chan<- C.PacketAdapter) (*UDPListener, error) { return NewUDPWithInfos(addr, "DEFAULT-TPROXY", "", in) } -func NewUDPWithInfos(addr, name, preferRulesName string, in chan<- C.PacketAdapter) (*UDPListener, error) { +func NewUDPWithInfos(addr, name, specialRules string, in chan<- C.PacketAdapter) (*UDPListener, error) { l, err := net.ListenPacket("udp", addr) if err != nil { return nil, err @@ -83,14 +83,14 @@ func NewUDPWithInfos(addr, name, preferRulesName string, in chan<- C.PacketAdapt // try to unmap 4in6 address lAddr = netip.AddrPortFrom(lAddr.Addr().Unmap(), lAddr.Port()) } - handlePacketConn(rl.name, rl.preferRulesName, l, in, buf[:n], lAddr, rAddr) + handlePacketConn(rl.name, rl.specialRules, l, in, buf[:n], lAddr, rAddr) } }() return rl, nil } -func handlePacketConn(name, preferRulesName string, pc net.PacketConn, in chan<- C.PacketAdapter, buf []byte, lAddr, rAddr netip.AddrPort) { +func handlePacketConn(name, specialRules string, pc net.PacketConn, in chan<- C.PacketAdapter, buf []byte, lAddr, rAddr netip.AddrPort) { target := socks5.AddrFromStdAddrPort(rAddr) pkt := &packet{ pc: pc, @@ -98,7 +98,7 @@ func handlePacketConn(name, preferRulesName string, pc net.PacketConn, in chan<- buf: buf, } select { - case in <- inbound.NewPacketWithInfos(target, pkt, C.TPROXY, name, preferRulesName): + case in <- inbound.NewPacketWithInfos(target, pkt, C.TPROXY, name, specialRules): default: } } diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 943bd917..64ed0325 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -508,7 +508,7 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { } func getRules(metadata *C.Metadata) []C.Rule { - if sr, ok := subRules[metadata.PreferRulesName]; ok { + if sr, ok := subRules[metadata.SpecialRules]; ok { return sr } else { return rules From c7f83d3ff19e56e4f2d2b9c108668dc86ca27a10 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 4 Dec 2022 23:05:13 +0800 Subject: [PATCH 197/250] chore: listeners support tuic --- common/net/tls.go | 7 ++-- docs/config.yaml | 15 +++++++ listener/inbound/tuic.go | 86 ++++++++++++++++++++++++++++++++++++++++ listener/listener.go | 3 ++ listener/parse.go | 12 ++++++ listener/tuic/server.go | 34 ++++++++++++---- 6 files changed, 147 insertions(+), 10 deletions(-) create mode 100644 listener/inbound/tuic.go diff --git a/common/net/tls.go b/common/net/tls.go index 65391666..4f5263b8 100644 --- a/common/net/tls.go +++ b/common/net/tls.go @@ -4,7 +4,8 @@ import ( "crypto/tls" "fmt" ) -func ParseCert(certificate,privateKey string) (tls.Certificate, error) { + +func ParseCert(certificate, privateKey string) (tls.Certificate, error) { cert, painTextErr := tls.X509KeyPair([]byte(certificate), []byte(privateKey)) if painTextErr == nil { return cert, nil @@ -12,7 +13,7 @@ func ParseCert(certificate,privateKey string) (tls.Certificate, error) { cert, loadErr := tls.LoadX509KeyPair(certificate, privateKey) if loadErr != nil { - return tls.Certificate{}, fmt.Errorf("parse certificate failed,maybe format error:%s, or path error: %s", painTextErr.Error(), loadErr.Error()) + return tls.Certificate{}, fmt.Errorf("parse certificate failed, maybe format error:%s, or path error: %s", painTextErr.Error(), loadErr.Error()) } return cert, nil -} \ No newline at end of file +} diff --git a/docs/config.yaml b/docs/config.yaml index 9507bbb8..e58746e0 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -695,3 +695,18 @@ listeners: listen: 0.0.0.0 # udp: false # 默认 true # rule: sub-rule + + - name: tuic-in-1 + type: tuic + port: 10813 + listen: 0.0.0.0 + # token: + # - TOKEN + # certificate: ./server.crt + # private-key: ./server.key + # congestion-controller: bbr + # max-idle-time: 15000 + # authentication-timeout: 1000 + # alpn: + # - h3 + # max-udp-relay-packet-size: 1500 diff --git a/listener/inbound/tuic.go b/listener/inbound/tuic.go new file mode 100644 index 00000000..e64d53bf --- /dev/null +++ b/listener/inbound/tuic.go @@ -0,0 +1,86 @@ +package inbound + +import ( + C "github.com/Dreamacro/clash/constant" + LC "github.com/Dreamacro/clash/listener/config" + "github.com/Dreamacro/clash/listener/tuic" + "github.com/Dreamacro/clash/log" +) + +type TuicOption struct { + BaseOption + Token []string `inbound:"token"` + Certificate string `inbound:"certificate"` + PrivateKey string `inbound:"private-key"` + CongestionController string `inbound:"congestion-controllerr,omitempty"` + MaxIdleTime int `inbound:"max-idle-timer,omitempty"` + AuthenticationTimeout int `inbound:"authentication-timeoutr,omitempty"` + ALPN []string `inbound:"alpnr,omitempty"` + MaxUdpRelayPacketSize int `inbound:"max-udp-relay-packet-sizer,omitempty"` +} + +func (o TuicOption) Equal(config C.InboundConfig) bool { + return optionToString(o) == optionToString(config) +} + +type Tuic struct { + *Base + config *TuicOption + l *tuic.Listener +} + +func NewTuic(options *TuicOption) (*Tuic, error) { + base, err := NewBase(&options.BaseOption) + if err != nil { + return nil, err + } + return &Tuic{ + Base: base, + config: options, + }, nil + +} + +// Config implements constant.InboundListener +func (t *Tuic) Config() C.InboundConfig { + return t.config +} + +// Address implements constant.InboundListener +func (t *Tuic) Address() string { + if t.l != nil { + for _, addr := range t.l.AddrList() { + return addr.String() + } + } + return "" +} + +// Listen implements constant.InboundListener +func (t *Tuic) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { + var err error + t.l, err = tuic.New(LC.TuicServer{ + Enable: true, + Listen: t.RawAddress(), + Token: t.config.Token, + Certificate: t.config.Certificate, + PrivateKey: t.config.PrivateKey, + CongestionController: t.config.CongestionController, + MaxIdleTime: t.config.MaxIdleTime, + AuthenticationTimeout: t.config.AuthenticationTimeout, + ALPN: t.config.ALPN, + MaxUdpRelayPacketSize: t.config.MaxUdpRelayPacketSize, + }, tcpIn, udpIn) + if err != nil { + return err + } + log.Infoln("Tuic[%s] proxy listening at: %s", t.Name(), t.Address()) + return nil +} + +// Close implements constant.InboundListener +func (t *Tuic) Close() error { + return t.l.Close() +} + +var _ C.InboundListener = (*Tuic)(nil) diff --git a/listener/listener.go b/listener/listener.go index 4c976b31..18b13b7f 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -371,6 +371,9 @@ func ReCreateTuic(config LC.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- tuicListener = listener + for _, addr := range tuicListener.AddrList() { + log.Infoln("Tuic proxy listening at: %s", addr.String()) + } return } diff --git a/listener/parse.go b/listener/parse.go index 04573c71..6270daf1 100644 --- a/listener/parse.go +++ b/listener/parse.go @@ -55,6 +55,18 @@ func ParseListener(mapping map[string]any) (C.InboundListener, error) { return nil, err } listener, err = IN.NewMixed(mixedOption) + case "tuic": + tuicOption := &IN.TuicOption{ + MaxIdleTime: 15000, + AuthenticationTimeout: 1000, + ALPN: []string{"h3"}, + MaxUdpRelayPacketSize: 1500, + } + err = decoder.Decode(mapping, tuicOption) + if err != nil { + return nil, err + } + listener, err = IN.NewTuic(tuicOption) default: return nil, fmt.Errorf("unsupport proxy type: %s", proxyType) } diff --git a/listener/tuic/server.go b/listener/tuic/server.go index 82f208cb..1e7209a8 100644 --- a/listener/tuic/server.go +++ b/listener/tuic/server.go @@ -9,6 +9,7 @@ import ( "github.com/metacubex/quic-go" "github.com/Dreamacro/clash/adapter/inbound" + CN "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/sockopt" C "github.com/Dreamacro/clash/constant" LC "github.com/Dreamacro/clash/listener/config" @@ -25,7 +26,11 @@ type Listener struct { } func New(config LC.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) (*Listener, error) { - cert, err := tls.LoadX509KeyPair(config.Certificate, config.PrivateKey) + return NewWithInfos("DEFAULT-TUIC", "", config, tcpIn, udpIn) +} + +func NewWithInfos(name, specialRules string, config LC.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) (*Listener, error) { + cert, err := CN.ParseCert(config.Certificate, config.PrivateKey) if err != nil { return nil, err } @@ -56,12 +61,12 @@ func New(config LC.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.Packet option := &tuic.ServerOption{ HandleTcpFn: func(conn net.Conn, addr socks5.Addr) error { - tcpIn <- inbound.NewSocket(addr, conn, C.TUIC) + tcpIn <- inbound.NewSocketWithInfos(addr, conn, C.TUIC, name, specialRules) return nil }, HandleUdpFn: func(addr socks5.Addr, packet C.UDPPacket) error { select { - case udpIn <- inbound.NewPacket(addr, packet, C.TUIC): + case udpIn <- inbound.NewPacketWithInfos(addr, packet, C.TUIC, name, specialRules): default: } return nil @@ -99,7 +104,6 @@ func New(config LC.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.Packet sl.servers = append(sl.servers, server) go func() { - log.Infoln("Tuic proxy listening at: %s", ul.LocalAddr().String()) err := server.Serve() if err != nil { if sl.closed { @@ -112,16 +116,32 @@ func New(config LC.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.Packet return sl, nil } -func (l *Listener) Close() { +// Close implements C.Listener +func (l *Listener) Close() error { l.closed = true + var retErr error for _, lis := range l.servers { - _ = lis.Close() + err := lis.Close() + if err != nil { + retErr = err + } } for _, lis := range l.udpListeners { - _ = lis.Close() + err := lis.Close() + if err != nil { + retErr = err + } } + return retErr } func (l *Listener) Config() LC.TuicServer { return l.config } + +func (l *Listener) AddrList() (addrList []net.Addr) { + for _, lis := range l.udpListeners { + addrList = append(addrList, lis.LocalAddr()) + } + return +} \ No newline at end of file From 2e22c712af47bbe4e7d3046159cedfbd47c99a83 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 5 Dec 2022 00:20:50 +0800 Subject: [PATCH 198/250] chore: rebuild add adapter/inbound.Addition to simply Listener.New apis --- adapter/inbound/addition.go | 15 +++++++++++ adapter/inbound/http.go | 11 +++----- adapter/inbound/https.go | 11 +++----- adapter/inbound/packet.go | 13 ++++----- adapter/inbound/socket.go | 14 +++++----- constant/listener.go | 3 ++- listener/autoredir/tcp.go | 32 +++++++++++----------- listener/http/client.go | 4 +-- listener/http/proxy.go | 8 +++--- listener/http/server.go | 32 +++++++++++----------- listener/http/upgrade.go | 4 +-- listener/inbound/base.go | 22 +++++++++++---- listener/inbound/http.go | 2 +- listener/inbound/mixed.go | 4 +-- listener/inbound/redir.go | 2 +- listener/inbound/socks.go | 4 +-- listener/inbound/tproxy.go | 4 +-- listener/inbound/tuic.go | 29 +++++++++++--------- listener/mixed/mixed.go | 40 +++++++++++++-------------- listener/redir/tcp.go | 32 +++++++++++----------- listener/shadowsocks/tcp.go | 25 ++++++++++++++--- listener/shadowsocks/udp.go | 4 +++ listener/sing_shadowsocks/server.go | 24 ++++++++++++++--- listener/sing_tun/server.go | 4 +-- listener/sing_vmess/server.go | 21 ++++++++++++--- listener/socks/tcp.go | 42 ++++++++++++++--------------- listener/socks/udp.go | 32 +++++++++++----------- listener/tproxy/tproxy.go | 32 +++++++++++----------- listener/tproxy/udp.go | 26 +++++++++--------- listener/tuic/server.go | 19 ++++++------- 30 files changed, 290 insertions(+), 225 deletions(-) create mode 100644 adapter/inbound/addition.go diff --git a/adapter/inbound/addition.go b/adapter/inbound/addition.go new file mode 100644 index 00000000..4d57fcee --- /dev/null +++ b/adapter/inbound/addition.go @@ -0,0 +1,15 @@ +package inbound + +import ( + C "github.com/Dreamacro/clash/constant" +) + +type Addition struct { + InName string + SpecialRules string +} + +func (a Addition) Apply(metadata *C.Metadata) { + metadata.InName = a.InName + metadata.SpecialRules = a.SpecialRules +} diff --git a/adapter/inbound/http.go b/adapter/inbound/http.go index 0637b39a..94040078 100644 --- a/adapter/inbound/http.go +++ b/adapter/inbound/http.go @@ -9,16 +9,13 @@ import ( ) // NewHTTP receive normal http request and return HTTPContext -func NewHTTP(target socks5.Addr, source net.Addr, conn net.Conn) *context.ConnContext { - return NewHTTPWithInfos(target, source, conn, "", "") -} - -func NewHTTPWithInfos(target socks5.Addr, source net.Addr, conn net.Conn, inName, specialRules string) *context.ConnContext { +func NewHTTP(target socks5.Addr, source net.Addr, conn net.Conn, additions ...Addition) *context.ConnContext { metadata := parseSocksAddr(target) metadata.NetWork = C.TCP metadata.Type = C.HTTP - metadata.InName = inName - metadata.SpecialRules = specialRules + for _, addition := range additions { + addition.Apply(metadata) + } if ip, port, err := parseAddr(source.String()); err == nil { metadata.SrcIP = ip metadata.SrcPort = port diff --git a/adapter/inbound/https.go b/adapter/inbound/https.go index dbc17835..3fff2371 100644 --- a/adapter/inbound/https.go +++ b/adapter/inbound/https.go @@ -9,15 +9,12 @@ import ( ) // NewHTTPS receive CONNECT request and return ConnContext -func NewHTTPS(request *http.Request, conn net.Conn) *context.ConnContext { - return NewHTTPSWithInfos(request, conn, "", "") -} - -func NewHTTPSWithInfos(request *http.Request, conn net.Conn, inName, specialRules string) *context.ConnContext { +func NewHTTPS(request *http.Request, conn net.Conn, additions ...Addition) *context.ConnContext { metadata := parseHTTPAddr(request) metadata.Type = C.HTTPS - metadata.SpecialRules = specialRules - metadata.InName = inName + for _, addition := range additions { + addition.Apply(metadata) + } if ip, port, err := parseAddr(conn.RemoteAddr().String()); err == nil { metadata.SrcIP = ip metadata.SrcPort = port diff --git a/adapter/inbound/packet.go b/adapter/inbound/packet.go index a84a3008..d1fcb05d 100644 --- a/adapter/inbound/packet.go +++ b/adapter/inbound/packet.go @@ -16,12 +16,14 @@ func (s *PacketAdapter) Metadata() *C.Metadata { return s.metadata } -func NewPacketWithInfos(target socks5.Addr, packet C.UDPPacket, source C.Type, inName, specialRules string) C.PacketAdapter { +// NewPacket is PacketAdapter generator +func NewPacket(target socks5.Addr, packet C.UDPPacket, source C.Type, additions ...Addition) C.PacketAdapter { metadata := parseSocksAddr(target) metadata.NetWork = C.UDP metadata.Type = source - metadata.InName = inName - metadata.SpecialRules = specialRules + for _, addition := range additions { + addition.Apply(metadata) + } if ip, port, err := parseAddr(packet.LocalAddr().String()); err == nil { metadata.SrcIP = ip metadata.SrcPort = port @@ -38,8 +40,3 @@ func NewPacketWithInfos(target socks5.Addr, packet C.UDPPacket, source C.Type, i metadata, } } - -// NewPacket is PacketAdapter generator -func NewPacket(target socks5.Addr, packet C.UDPPacket, source C.Type) C.PacketAdapter { - return NewPacketWithInfos(target, packet, source, "", "") -} diff --git a/adapter/inbound/socket.go b/adapter/inbound/socket.go index 34f31cf3..557b22ab 100644 --- a/adapter/inbound/socket.go +++ b/adapter/inbound/socket.go @@ -9,12 +9,15 @@ import ( "github.com/Dreamacro/clash/transport/socks5" ) -func NewSocketWithInfos(target socks5.Addr, conn net.Conn, source C.Type, inName, specialRules string) *context.ConnContext { +// NewSocket receive TCP inbound and return ConnContext +func NewSocket(target socks5.Addr, conn net.Conn, source C.Type, additions ...Addition) *context.ConnContext { metadata := parseSocksAddr(target) metadata.NetWork = C.TCP metadata.Type = source - metadata.SpecialRules = specialRules - metadata.InName = inName + for _, addition := range additions { + addition.Apply(metadata) + } + remoteAddr := conn.RemoteAddr() // Filter when net.Addr interface is nil @@ -36,11 +39,6 @@ func NewSocketWithInfos(target socks5.Addr, conn net.Conn, source C.Type, inName return context.NewConnContext(conn, metadata) } -// NewSocket receive TCP inbound and return ConnContext -func NewSocket(target socks5.Addr, conn net.Conn, source C.Type) *context.ConnContext { - return NewSocketWithInfos(target, conn, source, "", "") -} - func NewInner(conn net.Conn, dst string, host string) *context.ConnContext { metadata := &C.Metadata{} metadata.NetWork = C.TCP diff --git a/constant/listener.go b/constant/listener.go index 09ecdb76..cb7ab456 100644 --- a/constant/listener.go +++ b/constant/listener.go @@ -9,8 +9,9 @@ type Listener interface { } type AdvanceListener interface { - Close() + Close() error Config() string + AddrList() (addrList []net.Addr) HandleConn(conn net.Conn, in chan<- ConnContext) } diff --git a/listener/autoredir/tcp.go b/listener/autoredir/tcp.go index 5e675540..67b57022 100644 --- a/listener/autoredir/tcp.go +++ b/listener/autoredir/tcp.go @@ -11,12 +11,11 @@ import ( ) type Listener struct { - listener net.Listener - addr string - closed bool - name string - specialRules string - lookupFunc func(netip.AddrPort) (socks5.Addr, error) + listener net.Listener + addr string + closed bool + additions []inbound.Addition + lookupFunc func(netip.AddrPort) (socks5.Addr, error) } // RawAddress implements C.Listener @@ -58,23 +57,24 @@ func (l *Listener) handleRedir(conn net.Conn, in chan<- C.ConnContext) { _ = conn.(*net.TCPConn).SetKeepAlive(true) - in <- inbound.NewSocketWithInfos(target, conn, C.REDIR, l.name, l.specialRules) + in <- inbound.NewSocket(target, conn, C.REDIR, l.additions...) } -func New(addr string, in chan<- C.ConnContext) (*Listener, error) { - return NewWithInfos(addr, "DEFAULT-REDIR", "", in) -} - -func NewWithInfos(addr, name, specialRules string, in chan<- C.ConnContext) (*Listener, error) { +func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) { + if len(additions) == 0 { + additions = []inbound.Addition{{ + InName: "DEFAULT-REDIR", + SpecialRules: "", + }} + } l, err := net.Listen("tcp", addr) if err != nil { return nil, err } rl := &Listener{ - listener: l, - addr: addr, - name: name, - specialRules: specialRules, + listener: l, + addr: addr, + additions: additions, } go func() { diff --git a/listener/http/client.go b/listener/http/client.go index 64208e17..15c21f91 100644 --- a/listener/http/client.go +++ b/listener/http/client.go @@ -12,7 +12,7 @@ import ( "github.com/Dreamacro/clash/transport/socks5" ) -func newClient(source net.Addr, name, specialRules string, in chan<- C.ConnContext) *http.Client { +func newClient(source net.Addr, in chan<- C.ConnContext, additions ...inbound.Addition) *http.Client { return &http.Client{ Transport: &http.Transport{ // from http.DefaultTransport @@ -32,7 +32,7 @@ func newClient(source net.Addr, name, specialRules string, in chan<- C.ConnConte left, right := net.Pipe() - in <- inbound.NewHTTPWithInfos(dstAddr, source, right, name, specialRules) + in <- inbound.NewHTTP(dstAddr, source, right, additions...) return left, nil }, diff --git a/listener/http/proxy.go b/listener/http/proxy.go index afded98e..a95f7195 100644 --- a/listener/http/proxy.go +++ b/listener/http/proxy.go @@ -14,8 +14,8 @@ import ( "github.com/Dreamacro/clash/log" ) -func HandleConn(name, specialRules string, c net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[string, bool]) { - client := newClient(c.RemoteAddr(), name, specialRules, in) +func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[string, bool], additions ...inbound.Addition) { + client := newClient(c.RemoteAddr(), in, additions...) defer client.CloseIdleConnections() conn := N.NewBufferedConn(c) @@ -48,7 +48,7 @@ func HandleConn(name, specialRules string, c net.Conn, in chan<- C.ConnContext, break // close connection } - in <- inbound.NewHTTPSWithInfos(request, conn, name, specialRules) + in <- inbound.NewHTTPS(request, conn, additions...) return // hijack connection } @@ -61,7 +61,7 @@ func HandleConn(name, specialRules string, c net.Conn, in chan<- C.ConnContext, request.RequestURI = "" if isUpgradeRequest(request) { - handleUpgrade(name, specialRules, conn, request, in) + handleUpgrade(conn, request, in, additions...) return // hijack connection } diff --git a/listener/http/server.go b/listener/http/server.go index fa51ba28..c8379955 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -9,11 +9,9 @@ import ( ) type Listener struct { - listener net.Listener - addr string - closed bool - name string - specialRules string + listener net.Listener + addr string + closed bool } // RawAddress implements C.Listener @@ -32,15 +30,17 @@ func (l *Listener) Close() error { return l.listener.Close() } -func New(addr string, in chan<- C.ConnContext) (*Listener, error) { - return NewWithAuthenticate(addr, "DEFAULT-HTTP", "", in, true) +func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) { + return NewWithAuthenticate(addr, in, true, additions...) } -func NewWithInfos(addr, name, specialRules string, in chan<- C.ConnContext) (*Listener, error) { - return NewWithAuthenticate(addr, name, specialRules, in, true) -} - -func NewWithAuthenticate(addr, name, specialRules string, in chan<- C.ConnContext, authenticate bool) (*Listener, error) { +func NewWithAuthenticate(addr string, in chan<- C.ConnContext, authenticate bool, additions ...inbound.Addition) (*Listener, error) { + if len(additions) == 0 { + additions = []inbound.Addition{{ + InName: "DEFAULT-HTTP", + SpecialRules: "", + }} + } l, err := inbound.Listen("tcp", addr) if err != nil { @@ -53,10 +53,8 @@ func NewWithAuthenticate(addr, name, specialRules string, in chan<- C.ConnContex } hl := &Listener{ - listener: l, - name: name, - specialRules: specialRules, - addr: addr, + listener: l, + addr: addr, } go func() { for { @@ -67,7 +65,7 @@ func NewWithAuthenticate(addr, name, specialRules string, in chan<- C.ConnContex } continue } - go HandleConn(hl.name, hl.specialRules, conn, in, c) + go HandleConn(conn, in, c, additions...) } }() diff --git a/listener/http/upgrade.go b/listener/http/upgrade.go index a4601995..90e28f0a 100644 --- a/listener/http/upgrade.go +++ b/listener/http/upgrade.go @@ -25,7 +25,7 @@ func isUpgradeRequest(req *http.Request) bool { return false } -func handleUpgrade(name, specialRules string, conn net.Conn, request *http.Request, in chan<- C.ConnContext) { +func handleUpgrade(conn net.Conn, request *http.Request, in chan<- C.ConnContext, additions ...inbound.Addition) { defer conn.Close() removeProxyHeaders(request.Header) @@ -43,7 +43,7 @@ func handleUpgrade(name, specialRules string, conn net.Conn, request *http.Reque left, right := net.Pipe() - in <- inbound.NewHTTPWithInfos(dstAddr, conn.RemoteAddr(), right, name, specialRules) + in <- inbound.NewHTTP(dstAddr, conn.RemoteAddr(), right, additions...) var bufferedLeft *N.BufferedConn if request.TLS != nil { diff --git a/listener/inbound/base.go b/listener/inbound/base.go index 7428e716..df61b0ea 100644 --- a/listener/inbound/base.go +++ b/listener/inbound/base.go @@ -6,6 +6,7 @@ import ( "net/netip" "strconv" + "github.com/Dreamacro/clash/adapter/inbound" C "github.com/Dreamacro/clash/constant" ) @@ -28,7 +29,7 @@ func NewBase(options *BaseOption) (*Base, error) { return &Base{ name: options.Name(), listenAddr: addr, - specialRules: options.PreferRulesName, + specialRules: options.SpecialRules, port: options.Port, config: options, }, nil @@ -64,13 +65,17 @@ func (*Base) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) er return nil } +func (b *Base) Additions() []inbound.Addition { + return b.config.Additions() +} + var _ C.InboundListener = (*Base)(nil) type BaseOption struct { - NameStr string `inbound:"name"` - Listen string `inbound:"listen,omitempty"` - Port int `inbound:"port"` - PreferRulesName string `inbound:"rule,omitempty"` + NameStr string `inbound:"name"` + Listen string `inbound:"listen,omitempty"` + Port int `inbound:"port"` + SpecialRules string `inbound:"rule,omitempty"` } func (o BaseOption) Name() string { @@ -81,6 +86,13 @@ func (o BaseOption) Equal(config C.InboundConfig) bool { return optionToString(o) == optionToString(config) } +func (o BaseOption) Additions() []inbound.Addition { + return []inbound.Addition{{ + InName: o.NameStr, + SpecialRules: o.SpecialRules, + }} +} + var _ C.InboundConfig = (*BaseOption)(nil) func optionToString(option any) string { diff --git a/listener/inbound/http.go b/listener/inbound/http.go index b6edb479..b19f0154 100644 --- a/listener/inbound/http.go +++ b/listener/inbound/http.go @@ -44,7 +44,7 @@ func (h *HTTP) Address() string { // Listen implements constant.InboundListener func (h *HTTP) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error - h.l, err = http.NewWithInfos(h.RawAddress(), h.name, h.specialRules, tcpIn) + h.l, err = http.New(h.RawAddress(), tcpIn, h.Additions()...) if err != nil { return err } diff --git a/listener/inbound/mixed.go b/listener/inbound/mixed.go index f29caebb..651ab486 100644 --- a/listener/inbound/mixed.go +++ b/listener/inbound/mixed.go @@ -52,12 +52,12 @@ func (m *Mixed) Address() string { // Listen implements constant.InboundListener func (m *Mixed) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error - m.l, err = mixed.NewWithInfos(m.RawAddress(), m.name, m.specialRules, tcpIn) + m.l, err = mixed.New(m.RawAddress(), tcpIn, m.Additions()...) if err != nil { return err } if m.udp { - m.lUDP, err = socks.NewUDPWithInfos(m.Address(), m.name, m.specialRules, udpIn) + m.lUDP, err = socks.NewUDP(m.Address(), udpIn, m.Additions()...) if err != nil { return err } diff --git a/listener/inbound/redir.go b/listener/inbound/redir.go index 9f0f35b4..37b5aab9 100644 --- a/listener/inbound/redir.go +++ b/listener/inbound/redir.go @@ -44,7 +44,7 @@ func (r *Redir) Address() string { // Listen implements constant.InboundListener func (r *Redir) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error - r.l, err = redir.NewWithInfos(r.Address(), r.name, r.specialRules, tcpIn) + r.l, err = redir.New(r.Address(), tcpIn, r.Additions()...) if err != nil { return err } diff --git a/listener/inbound/socks.go b/listener/inbound/socks.go index 9727350b..010d08f9 100644 --- a/listener/inbound/socks.go +++ b/listener/inbound/socks.go @@ -70,11 +70,11 @@ func (s *Socks) Address() string { // Listen implements constant.InboundListener func (s *Socks) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error - if s.stl, err = socks.NewWithInfos(s.RawAddress(), s.name, s.specialRules, tcpIn); err != nil { + if s.stl, err = socks.New(s.RawAddress(), tcpIn, s.Additions()...); err != nil { return err } if s.udp { - if s.sul, err = socks.NewUDPWithInfos(s.RawAddress(), s.name, s.specialRules, udpIn); err != nil { + if s.sul, err = socks.NewUDP(s.RawAddress(), udpIn, s.Additions()...); err != nil { return err } } diff --git a/listener/inbound/tproxy.go b/listener/inbound/tproxy.go index c3e34033..223ebd93 100644 --- a/listener/inbound/tproxy.go +++ b/listener/inbound/tproxy.go @@ -51,13 +51,13 @@ func (t *TProxy) Address() string { // Listen implements constant.InboundListener func (t *TProxy) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error - t.lTCP, err = tproxy.NewWithInfos(t.RawAddress(), t.name, t.specialRules, tcpIn) + t.lTCP, err = tproxy.New(t.RawAddress(), tcpIn, t.Additions()...) if err != nil { return err } if t.udp { if t.lUDP != nil { - t.lUDP, err = tproxy.NewUDPWithInfos(t.Address(), t.name, t.specialRules, udpIn) + t.lUDP, err = tproxy.NewUDP(t.Address(), udpIn, t.Additions()...) if err != nil { return err } diff --git a/listener/inbound/tuic.go b/listener/inbound/tuic.go index e64d53bf..efe5d8b2 100644 --- a/listener/inbound/tuic.go +++ b/listener/inbound/tuic.go @@ -59,18 +59,23 @@ func (t *Tuic) Address() string { // Listen implements constant.InboundListener func (t *Tuic) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error - t.l, err = tuic.New(LC.TuicServer{ - Enable: true, - Listen: t.RawAddress(), - Token: t.config.Token, - Certificate: t.config.Certificate, - PrivateKey: t.config.PrivateKey, - CongestionController: t.config.CongestionController, - MaxIdleTime: t.config.MaxIdleTime, - AuthenticationTimeout: t.config.AuthenticationTimeout, - ALPN: t.config.ALPN, - MaxUdpRelayPacketSize: t.config.MaxUdpRelayPacketSize, - }, tcpIn, udpIn) + t.l, err = tuic.New( + LC.TuicServer{ + Enable: true, + Listen: t.RawAddress(), + Token: t.config.Token, + Certificate: t.config.Certificate, + PrivateKey: t.config.PrivateKey, + CongestionController: t.config.CongestionController, + MaxIdleTime: t.config.MaxIdleTime, + AuthenticationTimeout: t.config.AuthenticationTimeout, + ALPN: t.config.ALPN, + MaxUdpRelayPacketSize: t.config.MaxUdpRelayPacketSize, + }, + tcpIn, + udpIn, + t.Additions()..., + ) if err != nil { return err } diff --git a/listener/mixed/mixed.go b/listener/mixed/mixed.go index 34b4b1af..47beaf65 100644 --- a/listener/mixed/mixed.go +++ b/listener/mixed/mixed.go @@ -14,12 +14,10 @@ import ( ) type Listener struct { - listener net.Listener - addr string - name string - specialRules string - cache *cache.LruCache[string, bool] - closed bool + listener net.Listener + addr string + cache *cache.LruCache[string, bool] + closed bool } // RawAddress implements C.Listener @@ -38,22 +36,22 @@ func (l *Listener) Close() error { return l.listener.Close() } -func New(addr string, in chan<- C.ConnContext) (*Listener, error) { - return NewWithInfos(addr, "DEFAULT-MIXED", "", in) -} - -func NewWithInfos(addr, name, specialRules string, in chan<- C.ConnContext) (*Listener, error) { +func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) { + if len(additions) == 0 { + additions = []inbound.Addition{{ + InName: "DEFAULT-MIXED", + SpecialRules: "", + }} + } l, err := inbound.Listen("tcp", addr) if err != nil { return nil, err } ml := &Listener{ - listener: l, - addr: addr, - name: name, - specialRules: specialRules, - cache: cache.New[string, bool](cache.WithAge[string, bool](30)), + listener: l, + addr: addr, + cache: cache.New[string, bool](cache.WithAge[string, bool](30)), } go func() { for { @@ -64,14 +62,14 @@ func NewWithInfos(addr, name, specialRules string, in chan<- C.ConnContext) (*Li } continue } - go handleConn(ml.name, ml.specialRules, c, in, ml.cache) + go handleConn(c, in, ml.cache, additions...) } }() return ml, nil } -func handleConn(name, specialRules string, conn net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[string, bool]) { +func handleConn(conn net.Conn, in chan<- C.ConnContext, cache *cache.LruCache[string, bool], additions ...inbound.Addition) { conn.(*net.TCPConn).SetKeepAlive(true) bufConn := N.NewBufferedConn(conn) @@ -82,10 +80,10 @@ func handleConn(name, specialRules string, conn net.Conn, in chan<- C.ConnContex switch head[0] { case socks4.Version: - socks.HandleSocks4(name, specialRules, bufConn, in) + socks.HandleSocks4(bufConn, in, additions...) case socks5.Version: - socks.HandleSocks5(name, specialRules, bufConn, in) + socks.HandleSocks5(bufConn, in, additions...) default: - http.HandleConn(name, specialRules, bufConn, in, cache) + http.HandleConn(bufConn, in, cache, additions...) } } diff --git a/listener/redir/tcp.go b/listener/redir/tcp.go index 150f1da5..6849605a 100644 --- a/listener/redir/tcp.go +++ b/listener/redir/tcp.go @@ -8,11 +8,9 @@ import ( ) type Listener struct { - listener net.Listener - addr string - closed bool - name string - specialRules string + listener net.Listener + addr string + closed bool } // RawAddress implements C.Listener @@ -31,20 +29,20 @@ func (l *Listener) Close() error { return l.listener.Close() } -func New(addr string, in chan<- C.ConnContext) (*Listener, error) { - return NewWithInfos(addr, "DEFAULT-REDIR", "", in) -} - -func NewWithInfos(addr, name, specialRules string, in chan<- C.ConnContext) (*Listener, error) { +func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) { + if len(additions) == 0 { + additions = []inbound.Addition{{ + InName: "DEFAULT-REDIR", + SpecialRules: "", + }} + } l, err := net.Listen("tcp", addr) if err != nil { return nil, err } rl := &Listener{ - listener: l, - addr: addr, - name: name, - specialRules: specialRules, + listener: l, + addr: addr, } go func() { @@ -56,18 +54,18 @@ func NewWithInfos(addr, name, specialRules string, in chan<- C.ConnContext) (*Li } continue } - go handleRedir(rl.name, rl.specialRules, c, in) + go handleRedir(c, in, additions...) } }() return rl, nil } -func handleRedir(name, specialRules string, conn net.Conn, in chan<- C.ConnContext) { +func handleRedir(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) { target, err := parserPacket(conn) if err != nil { conn.Close() return } conn.(*net.TCPConn).SetKeepAlive(true) - in <- inbound.NewSocketWithInfos(target, conn, C.REDIR, name, specialRules) + in <- inbound.NewSocket(target, conn, C.REDIR, additions...) } diff --git a/listener/shadowsocks/tcp.go b/listener/shadowsocks/tcp.go index ab235e18..9d4b13b3 100644 --- a/listener/shadowsocks/tcp.go +++ b/listener/shadowsocks/tcp.go @@ -71,20 +71,37 @@ func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter return sl, nil } -func (l *Listener) Close() { - l.closed = true +func (l *Listener) Close() error { + var retErr error for _, lis := range l.listeners { - _ = lis.Close() + err := lis.Close() + if err != nil { + retErr = err + } } for _, lis := range l.udpListeners { - _ = lis.Close() + err := lis.Close() + if err != nil { + retErr = err + } } + return retErr } func (l *Listener) Config() string { return l.config } +func (l *Listener) AddrList() (addrList []net.Addr) { + for _, lis := range l.listeners { + addrList = append(addrList, lis.Addr()) + } + for _, lis := range l.udpListeners { + addrList = append(addrList, lis.LocalAddr()) + } + return +} + func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext) { conn = l.pickCipher.StreamConn(conn) diff --git a/listener/shadowsocks/udp.go b/listener/shadowsocks/udp.go index 6357afa9..3f058406 100644 --- a/listener/shadowsocks/udp.go +++ b/listener/shadowsocks/udp.go @@ -53,6 +53,10 @@ func (l *UDPListener) Close() error { return l.packetConn.Close() } +func (l *UDPListener) LocalAddr() net.Addr { + return l.packetConn.LocalAddr() +} + func handleSocksUDP(pc net.PacketConn, in chan<- C.PacketAdapter, buf []byte, addr net.Addr) { tgtAddr := socks5.SplitAddr(buf) if tgtAddr == nil { diff --git a/listener/sing_shadowsocks/server.go b/listener/sing_shadowsocks/server.go index 967d64fa..c0f62f78 100644 --- a/listener/sing_shadowsocks/server.go +++ b/listener/sing_shadowsocks/server.go @@ -126,20 +126,38 @@ func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter return sl, nil } -func (l *Listener) Close() { +func (l *Listener) Close() error { l.closed = true + var retErr error for _, lis := range l.listeners { - _ = lis.Close() + err := lis.Close() + if err != nil { + retErr = err + } } for _, lis := range l.udpListeners { - _ = lis.Close() + err := lis.Close() + if err != nil { + retErr = err + } } + return retErr } func (l *Listener) Config() string { return l.config } +func (l *Listener) AddrList() (addrList []net.Addr) { + for _, lis := range l.listeners { + addrList = append(addrList, lis.Addr()) + } + for _, lis := range l.udpListeners { + addrList = append(addrList, lis.LocalAddr()) + } + return +} + func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext) { err := l.service.NewConnection(context.TODO(), conn, metadata.Metadata{ Protocol: "shadowsocks", diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index aeba8999..bebff736 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -272,9 +272,9 @@ func parseRange(uidRanges []ranges.Range[uint32], rangeList []string) ([]ranges. return uidRanges, nil } -func (l *Listener) Close() { +func (l *Listener) Close() error { l.closed = true - _ = common.Close( + return common.Close( l.tunStack, l.tunIf, l.defaultInterfaceMonitor, diff --git a/listener/sing_vmess/server.go b/listener/sing_vmess/server.go index 997fc6b0..ca1ecdbd 100644 --- a/listener/sing_vmess/server.go +++ b/listener/sing_vmess/server.go @@ -80,18 +80,33 @@ func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter return sl, nil } -func (l *Listener) Close() { +func (l *Listener) Close() error { l.closed = true + var retErr error for _, lis := range l.listeners { - _ = lis.Close() + err := lis.Close() + if err != nil { + retErr = err + } } - _ = l.service.Close() + err := l.service.Close() + if err != nil { + retErr = err + } + return retErr } func (l *Listener) Config() string { return l.config } +func (l *Listener) AddrList() (addrList []net.Addr) { + for _, lis := range l.listeners { + addrList = append(addrList, lis.Addr()) + } + return +} + func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext) { err := l.service.NewConnection(context.TODO(), conn, metadata.Metadata{ Protocol: "vmess", diff --git a/listener/socks/tcp.go b/listener/socks/tcp.go index dc0aff8f..e2cb9d9a 100644 --- a/listener/socks/tcp.go +++ b/listener/socks/tcp.go @@ -13,11 +13,9 @@ import ( ) type Listener struct { - listener net.Listener - addr string - closed bool - specialRules string - name string + listener net.Listener + addr string + closed bool } // RawAddress implements C.Listener @@ -36,21 +34,21 @@ func (l *Listener) Close() error { return l.listener.Close() } -func New(addr string, in chan<- C.ConnContext) (*Listener, error) { - return NewWithInfos(addr, "DEFAULT-SOCKS", "", in) -} - -func NewWithInfos(addr, name, specialRules string, in chan<- C.ConnContext) (*Listener, error) { +func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) { + if len(additions) == 0 { + additions = []inbound.Addition{{ + InName: "DEFAULT-SOCKS", + SpecialRules: "", + }} + } l, err := inbound.Listen("tcp", addr) if err != nil { return nil, err } sl := &Listener{ - listener: l, - addr: addr, - name: name, - specialRules: specialRules, + listener: l, + addr: addr, } go func() { for { @@ -61,14 +59,14 @@ func NewWithInfos(addr, name, specialRules string, in chan<- C.ConnContext) (*Li } continue } - go handleSocks(sl.name, sl.specialRules, c, in) + go handleSocks(c, in, additions...) } }() return sl, nil } -func handleSocks(name, specialRules string, conn net.Conn, in chan<- C.ConnContext) { +func handleSocks(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) { conn.(*net.TCPConn).SetKeepAlive(true) bufConn := N.NewBufferedConn(conn) head, err := bufConn.Peek(1) @@ -79,24 +77,24 @@ func handleSocks(name, specialRules string, conn net.Conn, in chan<- C.ConnConte switch head[0] { case socks4.Version: - HandleSocks4(name, specialRules, bufConn, in) + HandleSocks4(bufConn, in, additions...) case socks5.Version: - HandleSocks5(name, specialRules, bufConn, in) + HandleSocks5(bufConn, in, additions...) default: conn.Close() } } -func HandleSocks4(name, specialRules string, conn net.Conn, in chan<- C.ConnContext) { +func HandleSocks4(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) { addr, _, err := socks4.ServerHandshake(conn, authStore.Authenticator()) if err != nil { conn.Close() return } - in <- inbound.NewSocketWithInfos(socks5.ParseAddr(addr), conn, C.SOCKS4, name, specialRules) + in <- inbound.NewSocket(socks5.ParseAddr(addr), conn, C.SOCKS4, additions...) } -func HandleSocks5(name, specialRules string, conn net.Conn, in chan<- C.ConnContext) { +func HandleSocks5(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) { target, command, err := socks5.ServerHandshake(conn, authStore.Authenticator()) if err != nil { conn.Close() @@ -107,5 +105,5 @@ func HandleSocks5(name, specialRules string, conn net.Conn, in chan<- C.ConnCont io.Copy(io.Discard, conn) return } - in <- inbound.NewSocketWithInfos(target, conn, C.SOCKS5, name, specialRules) + in <- inbound.NewSocket(target, conn, C.SOCKS5, additions...) } diff --git a/listener/socks/udp.go b/listener/socks/udp.go index c7232d9d..a718e21b 100644 --- a/listener/socks/udp.go +++ b/listener/socks/udp.go @@ -12,11 +12,9 @@ import ( ) type UDPListener struct { - packetConn net.PacketConn - addr string - closed bool - name string - specialRules string + packetConn net.PacketConn + addr string + closed bool } // RawAddress implements C.Listener @@ -35,11 +33,13 @@ func (l *UDPListener) Close() error { return l.packetConn.Close() } -func NewUDP(addr string, in chan<- C.PacketAdapter) (*UDPListener, error) { - return NewUDPWithInfos(addr, "DEFAULT-SOCKS", "", in) -} - -func NewUDPWithInfos(addr, name, specialRules string, in chan<- C.PacketAdapter) (*UDPListener, error) { +func NewUDP(addr string, in chan<- C.PacketAdapter, additions ...inbound.Addition) (*UDPListener, error) { + if len(additions) == 0 { + additions = []inbound.Addition{{ + InName: "DEFAULT-SOCKS", + SpecialRules: "", + }} + } l, err := net.ListenPacket("udp", addr) if err != nil { return nil, err @@ -50,10 +50,8 @@ func NewUDPWithInfos(addr, name, specialRules string, in chan<- C.PacketAdapter) } sl := &UDPListener{ - packetConn: l, - addr: addr, - specialRules: specialRules, - name: name, + packetConn: l, + addr: addr, } go func() { for { @@ -66,14 +64,14 @@ func NewUDPWithInfos(addr, name, specialRules string, in chan<- C.PacketAdapter) } continue } - handleSocksUDP(sl.name, sl.specialRules, l, in, buf[:n], remoteAddr) + handleSocksUDP(l, in, buf[:n], remoteAddr, additions...) } }() return sl, nil } -func handleSocksUDP(name, specialRules string, pc net.PacketConn, in chan<- C.PacketAdapter, buf []byte, addr net.Addr) { +func handleSocksUDP(pc net.PacketConn, in chan<- C.PacketAdapter, buf []byte, addr net.Addr, additions ...inbound.Addition) { target, payload, err := socks5.DecodeUDPPacket(buf) if err != nil { // Unresolved UDP packet, return buffer to the pool @@ -87,7 +85,7 @@ func handleSocksUDP(name, specialRules string, pc net.PacketConn, in chan<- C.Pa bufRef: buf, } select { - case in <- inbound.NewPacketWithInfos(target, packet, C.SOCKS5, name, specialRules): + case in <- inbound.NewPacket(target, packet, C.SOCKS5, additions...): default: } } diff --git a/listener/tproxy/tproxy.go b/listener/tproxy/tproxy.go index 857eb5e0..59b8f090 100644 --- a/listener/tproxy/tproxy.go +++ b/listener/tproxy/tproxy.go @@ -9,11 +9,9 @@ import ( ) type Listener struct { - listener net.Listener - addr string - closed bool - name string - specialRules string + listener net.Listener + addr string + closed bool } // RawAddress implements C.Listener @@ -32,17 +30,19 @@ func (l *Listener) Close() error { return l.listener.Close() } -func (l *Listener) handleTProxy(name, specialRules string, conn net.Conn, in chan<- C.ConnContext) { +func (l *Listener) handleTProxy(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) { target := socks5.ParseAddrToSocksAddr(conn.LocalAddr()) conn.(*net.TCPConn).SetKeepAlive(true) - in <- inbound.NewSocketWithInfos(target, conn, C.TPROXY, name, specialRules) + in <- inbound.NewSocket(target, conn, C.TPROXY, additions...) } -func New(addr string, in chan<- C.ConnContext) (*Listener, error) { - return NewWithInfos(addr, "DEFAULT-TPROXY", "", in) -} - -func NewWithInfos(addr, name, specialRules string, in chan<- C.ConnContext) (*Listener, error) { +func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) { + if len(additions) == 0 { + additions = []inbound.Addition{{ + InName: "DEFAULT-TPROXY", + SpecialRules: "", + }} + } l, err := net.Listen("tcp", addr) if err != nil { return nil, err @@ -60,10 +60,8 @@ func NewWithInfos(addr, name, specialRules string, in chan<- C.ConnContext) (*Li } rl := &Listener{ - listener: l, - addr: addr, - name: name, - specialRules: specialRules, + listener: l, + addr: addr, } go func() { @@ -75,7 +73,7 @@ func NewWithInfos(addr, name, specialRules string, in chan<- C.ConnContext) (*Li } continue } - go rl.handleTProxy(rl.name, rl.specialRules, c, in) + go rl.handleTProxy(c, in, additions...) } }() diff --git a/listener/tproxy/udp.go b/listener/tproxy/udp.go index c25bf103..9488a668 100644 --- a/listener/tproxy/udp.go +++ b/listener/tproxy/udp.go @@ -11,11 +11,9 @@ import ( ) type UDPListener struct { - packetConn net.PacketConn - addr string - closed bool - name string - specialRules string + packetConn net.PacketConn + addr string + closed bool } // RawAddress implements C.Listener @@ -34,11 +32,13 @@ func (l *UDPListener) Close() error { return l.packetConn.Close() } -func NewUDP(addr string, in chan<- C.PacketAdapter) (*UDPListener, error) { - return NewUDPWithInfos(addr, "DEFAULT-TPROXY", "", in) -} - -func NewUDPWithInfos(addr, name, specialRules string, in chan<- C.PacketAdapter) (*UDPListener, error) { +func NewUDP(addr string, in chan<- C.PacketAdapter, additions ...inbound.Addition) (*UDPListener, error) { + if len(additions) == 0 { + additions = []inbound.Addition{{ + InName: "DEFAULT-TPROXY", + SpecialRules: "", + }} + } l, err := net.ListenPacket("udp", addr) if err != nil { return nil, err @@ -83,14 +83,14 @@ func NewUDPWithInfos(addr, name, specialRules string, in chan<- C.PacketAdapter) // try to unmap 4in6 address lAddr = netip.AddrPortFrom(lAddr.Addr().Unmap(), lAddr.Port()) } - handlePacketConn(rl.name, rl.specialRules, l, in, buf[:n], lAddr, rAddr) + handlePacketConn(l, in, buf[:n], lAddr, rAddr, additions...) } }() return rl, nil } -func handlePacketConn(name, specialRules string, pc net.PacketConn, in chan<- C.PacketAdapter, buf []byte, lAddr, rAddr netip.AddrPort) { +func handlePacketConn(pc net.PacketConn, in chan<- C.PacketAdapter, buf []byte, lAddr, rAddr netip.AddrPort, additions ...inbound.Addition) { target := socks5.AddrFromStdAddrPort(rAddr) pkt := &packet{ pc: pc, @@ -98,7 +98,7 @@ func handlePacketConn(name, specialRules string, pc net.PacketConn, in chan<- C. buf: buf, } select { - case in <- inbound.NewPacketWithInfos(target, pkt, C.TPROXY, name, specialRules): + case in <- inbound.NewPacket(target, pkt, C.TPROXY, additions...): default: } } diff --git a/listener/tuic/server.go b/listener/tuic/server.go index 1e7209a8..a5ed7151 100644 --- a/listener/tuic/server.go +++ b/listener/tuic/server.go @@ -25,11 +25,13 @@ type Listener struct { servers []*tuic.Server } -func New(config LC.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) (*Listener, error) { - return NewWithInfos("DEFAULT-TUIC", "", config, tcpIn, udpIn) -} - -func NewWithInfos(name, specialRules string, config LC.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) (*Listener, error) { +func New(config LC.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, additions ...inbound.Addition) (*Listener, error) { + if len(additions) == 0 { + additions = []inbound.Addition{{ + InName: "DEFAULT-TUIC", + SpecialRules: "", + }} + } cert, err := CN.ParseCert(config.Certificate, config.PrivateKey) if err != nil { return nil, err @@ -61,12 +63,12 @@ func NewWithInfos(name, specialRules string, config LC.TuicServer, tcpIn chan<- option := &tuic.ServerOption{ HandleTcpFn: func(conn net.Conn, addr socks5.Addr) error { - tcpIn <- inbound.NewSocketWithInfos(addr, conn, C.TUIC, name, specialRules) + tcpIn <- inbound.NewSocket(addr, conn, C.TUIC, additions...) return nil }, HandleUdpFn: func(addr socks5.Addr, packet C.UDPPacket) error { select { - case udpIn <- inbound.NewPacketWithInfos(addr, packet, C.TUIC, name, specialRules): + case udpIn <- inbound.NewPacket(addr, packet, C.TUIC, additions...): default: } return nil @@ -116,7 +118,6 @@ func NewWithInfos(name, specialRules string, config LC.TuicServer, tcpIn chan<- return sl, nil } -// Close implements C.Listener func (l *Listener) Close() error { l.closed = true var retErr error @@ -144,4 +145,4 @@ func (l *Listener) AddrList() (addrList []net.Addr) { addrList = append(addrList, lis.LocalAddr()) } return -} \ No newline at end of file +} From b7d976796aee23da6cb6ea71f2776bf997e3e81d Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 5 Dec 2022 10:12:53 +0800 Subject: [PATCH 199/250] chore: listeners support shadowsocks/vmess --- adapter/inbound/addition.go | 20 +++++-- constant/listener.go | 3 +- docs/config.yaml | 25 +++++++- listener/autoredir/tcp.go | 8 +-- listener/config/shadowsocks.go | 17 ++++++ listener/config/vmess.go | 22 +++++++ listener/http/server.go | 8 +-- listener/inbound/base.go | 8 +-- listener/inbound/shadowsocks.go | 79 +++++++++++++++++++++++++ listener/inbound/vmess.go | 91 +++++++++++++++++++++++++++++ listener/listener.go | 40 ++++++++++--- listener/mixed/mixed.go | 8 +-- listener/parse.go | 14 +++++ listener/redir/tcp.go | 8 +-- listener/shadowsocks/tcp.go | 26 ++++----- listener/sing/context.go | 24 ++++++++ listener/sing/sing.go | 23 ++++++-- listener/sing_shadowsocks/server.go | 58 ++++++++++-------- listener/sing_vmess/server.go | 56 +++++++++++------- listener/socks/tcp.go | 8 +-- listener/socks/udp.go | 8 +-- listener/tproxy/tproxy.go | 8 +-- listener/tproxy/udp.go | 8 +-- listener/tuic/server.go | 8 +-- 24 files changed, 453 insertions(+), 125 deletions(-) create mode 100644 listener/config/shadowsocks.go create mode 100644 listener/config/vmess.go create mode 100644 listener/inbound/shadowsocks.go create mode 100644 listener/inbound/vmess.go create mode 100644 listener/sing/context.go diff --git a/adapter/inbound/addition.go b/adapter/inbound/addition.go index 4d57fcee..8bf1e786 100644 --- a/adapter/inbound/addition.go +++ b/adapter/inbound/addition.go @@ -4,12 +4,20 @@ import ( C "github.com/Dreamacro/clash/constant" ) -type Addition struct { - InName string - SpecialRules string -} +type Addition func(metadata *C.Metadata) func (a Addition) Apply(metadata *C.Metadata) { - metadata.InName = a.InName - metadata.SpecialRules = a.SpecialRules + a(metadata) +} + +func WithInName(name string) Addition { + return func(metadata *C.Metadata) { + metadata.InName = name + } +} + +func WithSpecialRules(specialRules string) Addition { + return func(metadata *C.Metadata) { + metadata.SpecialRules = specialRules + } } diff --git a/constant/listener.go b/constant/listener.go index cb7ab456..a52c1946 100644 --- a/constant/listener.go +++ b/constant/listener.go @@ -8,11 +8,10 @@ type Listener interface { Close() error } -type AdvanceListener interface { +type MultiAddrListener interface { Close() error Config() string AddrList() (addrList []net.Addr) - HandleConn(conn net.Conn, in chan<- ConnContext) } type InboundListener interface { diff --git a/docs/config.yaml b/docs/config.yaml index e58746e0..c20228db 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -121,7 +121,7 @@ tunnels: - tcp/udp,127.0.0.1:6553,114.114.114.114:53,proxy - tcp,127.0.0.1:6666,rds.mysql.com:3306,vpn # full yaml config - - network: [tcp, udp] + - network: [ tcp, udp ] address: 127.0.0.1:7777 target: target.com proxy: proxy @@ -693,13 +693,32 @@ listeners: type: tproxy port: 10812 listen: 0.0.0.0 - # udp: false # 默认 true # rule: sub-rule + # udp: false # 默认 true + + - name: shadowsocks-in-1 + type: shadowsocks + port: 10813 + listen: 0.0.0.0 + # rule: sub-rule + password: vlmpIPSyHH6f4S8WVPdRIHIlzmB+GIRfoH3aNJ/t9Gg= + cipher: 2022-blake3-aes-256-gcm + + - name: vmess-in-1 + type: vmess + port: 10814 + listen: 0.0.0.0 + # rule: sub-rule + users: + - username: 1 + uuid: 9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68 + alterId: 1 - name: tuic-in-1 type: tuic - port: 10813 + port: 10815 listen: 0.0.0.0 + # rule: sub-rule # token: # - TOKEN # certificate: ./server.crt diff --git a/listener/autoredir/tcp.go b/listener/autoredir/tcp.go index 67b57022..854d31d6 100644 --- a/listener/autoredir/tcp.go +++ b/listener/autoredir/tcp.go @@ -62,10 +62,10 @@ func (l *Listener) handleRedir(conn net.Conn, in chan<- C.ConnContext) { func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) { if len(additions) == 0 { - additions = []inbound.Addition{{ - InName: "DEFAULT-REDIR", - SpecialRules: "", - }} + additions = []inbound.Addition{ + inbound.WithInName("DEFAULT-REDIR"), + inbound.WithSpecialRules(""), + } } l, err := net.Listen("tcp", addr) if err != nil { diff --git a/listener/config/shadowsocks.go b/listener/config/shadowsocks.go new file mode 100644 index 00000000..cfe31f62 --- /dev/null +++ b/listener/config/shadowsocks.go @@ -0,0 +1,17 @@ +package config + +import ( + "encoding/json" +) + +type ShadowsocksServer struct { + Enable bool + Listen string + Password string + Cipher string +} + +func (t ShadowsocksServer) String() string { + b, _ := json.Marshal(t) + return string(b) +} diff --git a/listener/config/vmess.go b/listener/config/vmess.go new file mode 100644 index 00000000..cc49433e --- /dev/null +++ b/listener/config/vmess.go @@ -0,0 +1,22 @@ +package config + +import ( + "encoding/json" +) + +type VmessUser struct { + Username string + UUID string + AlterID int +} + +type VmessServer struct { + Enable bool + Listen string + Users []VmessUser +} + +func (t VmessServer) String() string { + b, _ := json.Marshal(t) + return string(b) +} diff --git a/listener/http/server.go b/listener/http/server.go index c8379955..8819af11 100644 --- a/listener/http/server.go +++ b/listener/http/server.go @@ -36,10 +36,10 @@ func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (* func NewWithAuthenticate(addr string, in chan<- C.ConnContext, authenticate bool, additions ...inbound.Addition) (*Listener, error) { if len(additions) == 0 { - additions = []inbound.Addition{{ - InName: "DEFAULT-HTTP", - SpecialRules: "", - }} + additions = []inbound.Addition{ + inbound.WithInName("DEFAULT-HTTP"), + inbound.WithSpecialRules(""), + } } l, err := inbound.Listen("tcp", addr) diff --git a/listener/inbound/base.go b/listener/inbound/base.go index df61b0ea..46cd190d 100644 --- a/listener/inbound/base.go +++ b/listener/inbound/base.go @@ -87,10 +87,10 @@ func (o BaseOption) Equal(config C.InboundConfig) bool { } func (o BaseOption) Additions() []inbound.Addition { - return []inbound.Addition{{ - InName: o.NameStr, - SpecialRules: o.SpecialRules, - }} + return []inbound.Addition{ + inbound.WithInName(o.NameStr), + inbound.WithSpecialRules(o.SpecialRules), + } } var _ C.InboundConfig = (*BaseOption)(nil) diff --git a/listener/inbound/shadowsocks.go b/listener/inbound/shadowsocks.go new file mode 100644 index 00000000..c21f2f39 --- /dev/null +++ b/listener/inbound/shadowsocks.go @@ -0,0 +1,79 @@ +package inbound + +import ( + C "github.com/Dreamacro/clash/constant" + LC "github.com/Dreamacro/clash/listener/config" + "github.com/Dreamacro/clash/listener/sing_shadowsocks" + "github.com/Dreamacro/clash/log" +) + +type ShadowSocksOption struct { + BaseOption + Password string `inbound:"password"` + Cipher string `inbound:"cipher"` +} + +func (o ShadowSocksOption) Equal(config C.InboundConfig) bool { + return optionToString(o) == optionToString(config) +} + +type ShadowSocks struct { + *Base + config *ShadowSocksOption + l C.MultiAddrListener +} + +func NewShadowSocks(options *ShadowSocksOption) (*ShadowSocks, error) { + base, err := NewBase(&options.BaseOption) + if err != nil { + return nil, err + } + return &ShadowSocks{ + Base: base, + config: options, + }, nil + +} + +// Config implements constant.InboundListener +func (s *ShadowSocks) Config() C.InboundConfig { + return s.config +} + +// Address implements constant.InboundListener +func (s *ShadowSocks) Address() string { + if s.l != nil { + for _, addr := range s.l.AddrList() { + return addr.String() + } + } + return "" +} + +// Listen implements constant.InboundListener +func (s *ShadowSocks) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { + var err error + s.l, err = sing_shadowsocks.New( + LC.ShadowsocksServer{ + Enable: true, + Listen: s.RawAddress(), + Password: s.config.Password, + Cipher: s.config.Cipher, + }, + tcpIn, + udpIn, + s.Additions()..., + ) + if err != nil { + return err + } + log.Infoln("ShadowSocks[%s] proxy listening at: %s", s.Name(), s.Address()) + return nil +} + +// Close implements constant.InboundListener +func (s *ShadowSocks) Close() error { + return s.l.Close() +} + +var _ C.InboundListener = (*ShadowSocks)(nil) diff --git a/listener/inbound/vmess.go b/listener/inbound/vmess.go new file mode 100644 index 00000000..745da45e --- /dev/null +++ b/listener/inbound/vmess.go @@ -0,0 +1,91 @@ +package inbound + +import ( + C "github.com/Dreamacro/clash/constant" + LC "github.com/Dreamacro/clash/listener/config" + "github.com/Dreamacro/clash/listener/sing_vmess" + "github.com/Dreamacro/clash/log" +) + +type VmessOption struct { + BaseOption + Users []VmessUser `inbound:"users"` +} + +type VmessUser struct { + Username string `inbound:"username,omitempty"` + UUID string `inbound:"uuid"` + AlterID int `inbound:"alterId"` +} + +func (o VmessOption) Equal(config C.InboundConfig) bool { + return optionToString(o) == optionToString(config) +} + +type Vmess struct { + *Base + config *VmessOption + l C.MultiAddrListener +} + +func NewVmess(options *VmessOption) (*Vmess, error) { + base, err := NewBase(&options.BaseOption) + if err != nil { + return nil, err + } + return &Vmess{ + Base: base, + config: options, + }, nil + +} + +// Config implements constant.InboundListener +func (v *Vmess) Config() C.InboundConfig { + return v.config +} + +// Address implements constant.InboundListener +func (v *Vmess) Address() string { + if v.l != nil { + for _, addr := range v.l.AddrList() { + return addr.String() + } + } + return "" +} + +// Listen implements constant.InboundListener +func (v *Vmess) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { + var err error + users := make([]LC.VmessUser, len(v.config.Users)) + for i, v := range v.config.Users { + users[i] = LC.VmessUser{ + Username: v.Username, + UUID: v.UUID, + AlterID: v.AlterID, + } + } + v.l, err = sing_vmess.New( + LC.VmessServer{ + Enable: true, + Listen: v.RawAddress(), + Users: users, + }, + tcpIn, + udpIn, + v.Additions()..., + ) + if err != nil { + return err + } + log.Infoln("Vmess[%s] proxy listening at: %s", v.Name(), v.Address()) + return nil +} + +// Close implements constant.InboundListener +func (v *Vmess) Close() error { + return v.l.Close() +} + +var _ C.InboundListener = (*Vmess)(nil) diff --git a/listener/listener.go b/listener/listener.go index 18b13b7f..99e16aea 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -16,6 +16,7 @@ import ( "github.com/Dreamacro/clash/listener/http" "github.com/Dreamacro/clash/listener/mixed" "github.com/Dreamacro/clash/listener/redir" + embedSS "github.com/Dreamacro/clash/listener/shadowsocks" "github.com/Dreamacro/clash/listener/sing_shadowsocks" "github.com/Dreamacro/clash/listener/sing_tun" "github.com/Dreamacro/clash/listener/sing_vmess" @@ -45,7 +46,7 @@ var ( tunnelUDPListeners = map[string]*tunnel.PacketConn{} inboundListeners = map[string]C.InboundListener{} tunLister *sing_tun.Listener - shadowSocksListener C.AdvanceListener + shadowSocksListener C.MultiAddrListener vmessListener *sing_vmess.Listener tuicListener *tuic.Listener autoRedirListener *autoredir.Listener @@ -263,10 +264,20 @@ func ReCreateShadowSocks(shadowSocksConfig string, tcpIn chan<- C.ConnContext, u } }() + var ssConfig LC.ShadowsocksServer + if addr, cipher, password, err := embedSS.ParseSSURL(shadowSocksConfig); err == nil { + ssConfig = LC.ShadowsocksServer{ + Enable: true, + Listen: addr, + Password: password, + Cipher: cipher, + } + } + shouldIgnore := false if shadowSocksListener != nil { - if shadowSocksListener.Config() != shadowSocksConfig { + if shadowSocksListener.Config() != ssConfig.String() { shadowSocksListener.Close() shadowSocksListener = nil } else { @@ -278,17 +289,20 @@ func ReCreateShadowSocks(shadowSocksConfig string, tcpIn chan<- C.ConnContext, u return } - if len(shadowSocksConfig) == 0 { + if !ssConfig.Enable { return } - listener, err := sing_shadowsocks.New(shadowSocksConfig, tcpIn, udpIn) + listener, err := sing_shadowsocks.New(ssConfig, tcpIn, udpIn) if err != nil { return } shadowSocksListener = listener + for _, addr := range shadowSocksListener.AddrList() { + log.Infoln("ShadowSocks proxy listening at: %s", addr.String()) + } return } @@ -303,10 +317,19 @@ func ReCreateVmess(vmessConfig string, tcpIn chan<- C.ConnContext, udpIn chan<- } }() + var vsConfig LC.VmessServer + if addr, username, password, err := sing_vmess.ParseVmessURL(vmessConfig); err == nil { + vsConfig = LC.VmessServer{ + Enable: true, + Listen: addr, + Users: []LC.VmessUser{{Username: username, UUID: password, AlterID: 1}}, + } + } + shouldIgnore := false if vmessListener != nil { - if vmessListener.Config() != vmessConfig { + if vmessListener.Config() != vsConfig.String() { vmessListener.Close() vmessListener = nil } else { @@ -318,17 +341,20 @@ func ReCreateVmess(vmessConfig string, tcpIn chan<- C.ConnContext, udpIn chan<- return } - if len(vmessConfig) == 0 { + if !vsConfig.Enable { return } - listener, err := sing_vmess.New(vmessConfig, tcpIn, udpIn) + listener, err := sing_vmess.New(vsConfig, tcpIn, udpIn) if err != nil { return } vmessListener = listener + for _, addr := range vmessListener.AddrList() { + log.Infoln("Vmess proxy listening at: %s", addr.String()) + } return } diff --git a/listener/mixed/mixed.go b/listener/mixed/mixed.go index 47beaf65..e8385873 100644 --- a/listener/mixed/mixed.go +++ b/listener/mixed/mixed.go @@ -38,10 +38,10 @@ func (l *Listener) Close() error { func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) { if len(additions) == 0 { - additions = []inbound.Addition{{ - InName: "DEFAULT-MIXED", - SpecialRules: "", - }} + additions = []inbound.Addition{ + inbound.WithInName("DEFAULT-MIXED"), + inbound.WithSpecialRules(""), + } } l, err := inbound.Listen("tcp", addr) if err != nil { diff --git a/listener/parse.go b/listener/parse.go index 6270daf1..eef1df16 100644 --- a/listener/parse.go +++ b/listener/parse.go @@ -55,6 +55,20 @@ func ParseListener(mapping map[string]any) (C.InboundListener, error) { return nil, err } listener, err = IN.NewMixed(mixedOption) + case "shadowsocks": + shadowsocksOption := &IN.ShadowSocksOption{} + err = decoder.Decode(mapping, shadowsocksOption) + if err != nil { + return nil, err + } + listener, err = IN.NewShadowSocks(shadowsocksOption) + case "vmess": + vmessOption := &IN.VmessOption{} + err = decoder.Decode(mapping, vmessOption) + if err != nil { + return nil, err + } + listener, err = IN.NewVmess(vmessOption) case "tuic": tuicOption := &IN.TuicOption{ MaxIdleTime: 15000, diff --git a/listener/redir/tcp.go b/listener/redir/tcp.go index 6849605a..ad4a91bc 100644 --- a/listener/redir/tcp.go +++ b/listener/redir/tcp.go @@ -31,10 +31,10 @@ func (l *Listener) Close() error { func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) { if len(additions) == 0 { - additions = []inbound.Addition{{ - InName: "DEFAULT-REDIR", - SpecialRules: "", - }} + additions = []inbound.Addition{ + inbound.WithInName("DEFAULT-REDIR"), + inbound.WithSpecialRules(""), + } } l, err := net.Listen("tcp", addr) if err != nil { diff --git a/listener/shadowsocks/tcp.go b/listener/shadowsocks/tcp.go index 9d4b13b3..21db5b63 100644 --- a/listener/shadowsocks/tcp.go +++ b/listener/shadowsocks/tcp.go @@ -6,14 +6,14 @@ import ( "github.com/Dreamacro/clash/adapter/inbound" C "github.com/Dreamacro/clash/constant" - "github.com/Dreamacro/clash/log" + LC "github.com/Dreamacro/clash/listener/config" "github.com/Dreamacro/clash/transport/shadowsocks/core" "github.com/Dreamacro/clash/transport/socks5" ) type Listener struct { closed bool - config string + config LC.ShadowsocksServer listeners []net.Listener udpListeners []*UDPListener pickCipher core.Cipher @@ -21,13 +21,8 @@ type Listener struct { var _listener *Listener -func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) (*Listener, error) { - addr, cipher, password, err := ParseSSURL(config) - if err != nil { - return nil, err - } - - pickCipher, err := core.PickCipher(cipher, nil, password) +func New(config LC.ShadowsocksServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) (*Listener, error) { + pickCipher, err := core.PickCipher(config.Cipher, nil, config.Password) if err != nil { return nil, err } @@ -35,7 +30,7 @@ func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter sl := &Listener{false, config, nil, nil, pickCipher} _listener = sl - for _, addr := range strings.Split(addr, ",") { + for _, addr := range strings.Split(config.Listen, ",") { addr := addr //UDP @@ -53,7 +48,6 @@ func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter sl.listeners = append(sl.listeners, l) go func() { - log.Infoln("ShadowSocks proxy listening at: %s", l.Addr().String()) for { c, err := l.Accept() if err != nil { @@ -89,7 +83,7 @@ func (l *Listener) Close() error { } func (l *Listener) Config() string { - return l.config + return l.config.String() } func (l *Listener) AddrList() (addrList []net.Addr) { @@ -102,7 +96,7 @@ func (l *Listener) AddrList() (addrList []net.Addr) { return } -func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext) { +func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) { conn = l.pickCipher.StreamConn(conn) target, err := socks5.ReadAddr(conn, make([]byte, socks5.MaxAddrLen)) @@ -110,12 +104,12 @@ func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext) { _ = conn.Close() return } - in <- inbound.NewSocket(target, conn, C.SHADOWSOCKS) + in <- inbound.NewSocket(target, conn, C.SHADOWSOCKS, additions...) } -func HandleShadowSocks(conn net.Conn, in chan<- C.ConnContext) bool { +func HandleShadowSocks(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) bool { if _listener != nil && _listener.pickCipher != nil { - go _listener.HandleConn(conn, in) + go _listener.HandleConn(conn, in, additions...) return true } return false diff --git a/listener/sing/context.go b/listener/sing/context.go new file mode 100644 index 00000000..f7aed851 --- /dev/null +++ b/listener/sing/context.go @@ -0,0 +1,24 @@ +package sing + +import ( + "context" + + "github.com/Dreamacro/clash/adapter/inbound" +) + +type contextKey string + +var ctxKeyAdditions = contextKey("Additions") + +func WithAdditions(ctx context.Context, additions ...inbound.Addition) context.Context { + return context.WithValue(ctx, ctxKeyAdditions, additions) +} + +func getAdditions(ctx context.Context) []inbound.Addition { + if v := ctx.Value(ctxKeyAdditions); v != nil { + if a, ok := v.([]inbound.Addition); ok { + return a + } + } + return nil +} diff --git a/listener/sing/sing.go b/listener/sing/sing.go index 5b605373..e8aafa39 100644 --- a/listener/sing/sing.go +++ b/listener/sing/sing.go @@ -3,6 +3,7 @@ package sing import ( "context" "errors" + "golang.org/x/exp/slices" "net" "sync" "time" @@ -23,9 +24,10 @@ import ( const UDPTimeout = 5 * time.Minute type ListenerHandler struct { - TcpIn chan<- C.ConnContext - UdpIn chan<- C.PacketAdapter - Type C.Type + TcpIn chan<- C.ConnContext + UdpIn chan<- C.PacketAdapter + Type C.Type + Additions []inbound.Addition } type waitCloseConn struct { @@ -47,6 +49,11 @@ func (c *waitCloseConn) RemoteAddr() net.Addr { } func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error { + additions := h.Additions + if ctxAdditions := getAdditions(ctx); len(ctxAdditions) > 0 { + additions = slices.Clone(additions) + additions = append(additions, ctxAdditions...) + } switch metadata.Destination.Fqdn { case vmess.MuxDestination.Fqdn: return vmess.HandleMuxConnection(ctx, conn, h) @@ -58,11 +65,17 @@ func (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, meta wg := &sync.WaitGroup{} defer wg.Wait() // this goroutine must exit after conn.Close() wg.Add(1) - h.TcpIn <- inbound.NewSocket(target, &waitCloseConn{Conn: conn, wg: wg, rAddr: metadata.Source.TCPAddr()}, h.Type) + + h.TcpIn <- inbound.NewSocket(target, &waitCloseConn{Conn: conn, wg: wg, rAddr: metadata.Source.TCPAddr()}, h.Type, additions...) return nil } func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error { + additions := h.Additions + if ctxAdditions := getAdditions(ctx); len(ctxAdditions) > 0 { + additions = slices.Clone(additions) + additions = append(additions, ctxAdditions...) + } defer func() { _ = conn.Close() }() mutex := sync.Mutex{} conn2 := conn // a new interface to set nil in defer @@ -90,7 +103,7 @@ func (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network. buff: buff, } select { - case h.UdpIn <- inbound.NewPacket(target, packet, h.Type): + case h.UdpIn <- inbound.NewPacket(target, packet, h.Type, additions...): default: } } diff --git a/listener/sing_shadowsocks/server.go b/listener/sing_shadowsocks/server.go index c0f62f78..d89085cd 100644 --- a/listener/sing_shadowsocks/server.go +++ b/listener/sing_shadowsocks/server.go @@ -9,6 +9,7 @@ import ( "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/common/sockopt" C "github.com/Dreamacro/clash/constant" + LC "github.com/Dreamacro/clash/listener/config" embedSS "github.com/Dreamacro/clash/listener/shadowsocks" "github.com/Dreamacro/clash/listener/sing" "github.com/Dreamacro/clash/log" @@ -24,7 +25,7 @@ import ( type Listener struct { closed bool - config string + config LC.ShadowsocksServer listeners []net.Listener udpListeners []net.PacketConn service shadowsocks.Service @@ -32,39 +33,46 @@ type Listener struct { var _listener *Listener -func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) (C.AdvanceListener, error) { - addr, cipher, password, err := embedSS.ParseSSURL(config) - if err != nil { - return nil, err +func New(config LC.ShadowsocksServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, additions ...inbound.Addition) (C.MultiAddrListener, error) { + var sl *Listener + var err error + if len(additions) == 0 { + additions = []inbound.Addition{ + inbound.WithInName("DEFAULT-SHADOWSOCKS"), + inbound.WithSpecialRules(""), + } + defer func() { + _listener = sl + }() } + udpTimeout := int64(sing.UDPTimeout.Seconds()) h := &sing.ListenerHandler{ - TcpIn: tcpIn, - UdpIn: udpIn, - Type: C.SHADOWSOCKS, + TcpIn: tcpIn, + UdpIn: udpIn, + Type: C.SHADOWSOCKS, + Additions: additions, } - sl := &Listener{false, config, nil, nil, nil} + sl = &Listener{false, config, nil, nil, nil} switch { - case cipher == shadowsocks.MethodNone: + case config.Cipher == shadowsocks.MethodNone: sl.service = shadowsocks.NewNoneService(udpTimeout, h) - case common.Contains(shadowaead.List, cipher): - sl.service, err = shadowaead.NewService(cipher, nil, password, udpTimeout, h) - case common.Contains(shadowaead_2022.List, cipher): - sl.service, err = shadowaead_2022.NewServiceWithPassword(cipher, password, udpTimeout, h) + case common.Contains(shadowaead.List, config.Cipher): + sl.service, err = shadowaead.NewService(config.Cipher, nil, config.Password, udpTimeout, h) + case common.Contains(shadowaead_2022.List, config.Cipher): + sl.service, err = shadowaead_2022.NewServiceWithPassword(config.Cipher, config.Password, udpTimeout, h) default: - err = fmt.Errorf("shadowsocks: unsupported method: %s", cipher) + err = fmt.Errorf("shadowsocks: unsupported method: %s", config.Cipher) return embedSS.New(config, tcpIn, udpIn) } if err != nil { return nil, err } - _listener = sl - - for _, addr := range strings.Split(addr, ",") { + for _, addr := range strings.Split(config.Listen, ",") { addr := addr //UDP @@ -107,7 +115,6 @@ func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter sl.listeners = append(sl.listeners, l) go func() { - log.Infoln("ShadowSocks proxy listening at: %s", l.Addr().String()) for { c, err := l.Accept() if err != nil { @@ -145,7 +152,7 @@ func (l *Listener) Close() error { } func (l *Listener) Config() string { - return l.config + return l.config.String() } func (l *Listener) AddrList() (addrList []net.Addr) { @@ -158,8 +165,9 @@ func (l *Listener) AddrList() (addrList []net.Addr) { return } -func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext) { - err := l.service.NewConnection(context.TODO(), conn, metadata.Metadata{ +func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) { + ctx := sing.WithAdditions(context.TODO(), additions...) + err := l.service.NewConnection(ctx, conn, metadata.Metadata{ Protocol: "shadowsocks", Source: metadata.ParseSocksaddr(conn.RemoteAddr().String()), }) @@ -169,10 +177,10 @@ func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext) { } } -func HandleShadowSocks(conn net.Conn, in chan<- C.ConnContext) bool { +func HandleShadowSocks(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) bool { if _listener != nil && _listener.service != nil { - go _listener.HandleConn(conn, in) + go _listener.HandleConn(conn, in, additions...) return true } - return embedSS.HandleShadowSocks(conn, in) + return embedSS.HandleShadowSocks(conn, in, additions...) } diff --git a/listener/sing_vmess/server.go b/listener/sing_vmess/server.go index ca1ecdbd..859317f0 100644 --- a/listener/sing_vmess/server.go +++ b/listener/sing_vmess/server.go @@ -8,36 +8,51 @@ import ( "github.com/Dreamacro/clash/adapter/inbound" C "github.com/Dreamacro/clash/constant" + LC "github.com/Dreamacro/clash/listener/config" "github.com/Dreamacro/clash/listener/sing" - "github.com/Dreamacro/clash/log" vmess "github.com/sagernet/sing-vmess" + "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/metadata" ) type Listener struct { closed bool - config string + config LC.VmessServer listeners []net.Listener service *vmess.Service[string] } var _listener *Listener -func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) (*Listener, error) { - addr, username, password, err := parseVmessURL(config) - if err != nil { - return nil, err +func New(config LC.VmessServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, additions ...inbound.Addition) (sl *Listener, err error) { + if len(additions) == 0 { + additions = []inbound.Addition{ + inbound.WithInName("DEFAULT-VMESS"), + inbound.WithSpecialRules(""), + } + defer func() { + _listener = sl + }() } - h := &sing.ListenerHandler{ - TcpIn: tcpIn, - UdpIn: udpIn, - Type: C.VMESS, + TcpIn: tcpIn, + UdpIn: udpIn, + Type: C.VMESS, + Additions: additions, } service := vmess.NewService[string](h) - err = service.UpdateUsers([]string{username}, []string{password}, []int{1}) + err = service.UpdateUsers( + common.Map(config.Users, func(it LC.VmessUser) string { + return it.Username + }), + common.Map(config.Users, func(it LC.VmessUser) string { + return it.UUID + }), + common.Map(config.Users, func(it LC.VmessUser) int { + return it.AlterID + })) if err != nil { return nil, err } @@ -47,10 +62,9 @@ func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter return nil, err } - sl := &Listener{false, config, nil, service} - _listener = sl + sl = &Listener{false, config, nil, service} - for _, addr := range strings.Split(addr, ",") { + for _, addr := range strings.Split(config.Listen, ",") { addr := addr //TCP @@ -61,7 +75,6 @@ func New(config string, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter sl.listeners = append(sl.listeners, l) go func() { - log.Infoln("Vmess proxy listening at: %s", l.Addr().String()) for { c, err := l.Accept() if err != nil { @@ -97,7 +110,7 @@ func (l *Listener) Close() error { } func (l *Listener) Config() string { - return l.config + return l.config.String() } func (l *Listener) AddrList() (addrList []net.Addr) { @@ -107,8 +120,9 @@ func (l *Listener) AddrList() (addrList []net.Addr) { return } -func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext) { - err := l.service.NewConnection(context.TODO(), conn, metadata.Metadata{ +func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) { + ctx := sing.WithAdditions(context.TODO(), additions...) + err := l.service.NewConnection(ctx, conn, metadata.Metadata{ Protocol: "vmess", Source: metadata.ParseSocksaddr(conn.RemoteAddr().String()), }) @@ -118,15 +132,15 @@ func (l *Listener) HandleConn(conn net.Conn, in chan<- C.ConnContext) { } } -func HandleVmess(conn net.Conn, in chan<- C.ConnContext) bool { +func HandleVmess(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) bool { if _listener != nil && _listener.service != nil { - go _listener.HandleConn(conn, in) + go _listener.HandleConn(conn, in, additions...) return true } return false } -func parseVmessURL(s string) (addr, username, password string, err error) { +func ParseVmessURL(s string) (addr, username, password string, err error) { u, err := url.Parse(s) if err != nil { return diff --git a/listener/socks/tcp.go b/listener/socks/tcp.go index e2cb9d9a..cbaac987 100644 --- a/listener/socks/tcp.go +++ b/listener/socks/tcp.go @@ -36,10 +36,10 @@ func (l *Listener) Close() error { func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) { if len(additions) == 0 { - additions = []inbound.Addition{{ - InName: "DEFAULT-SOCKS", - SpecialRules: "", - }} + additions = []inbound.Addition{ + inbound.WithInName("DEFAULT-SOCKS"), + inbound.WithSpecialRules(""), + } } l, err := inbound.Listen("tcp", addr) if err != nil { diff --git a/listener/socks/udp.go b/listener/socks/udp.go index a718e21b..f375dade 100644 --- a/listener/socks/udp.go +++ b/listener/socks/udp.go @@ -35,10 +35,10 @@ func (l *UDPListener) Close() error { func NewUDP(addr string, in chan<- C.PacketAdapter, additions ...inbound.Addition) (*UDPListener, error) { if len(additions) == 0 { - additions = []inbound.Addition{{ - InName: "DEFAULT-SOCKS", - SpecialRules: "", - }} + additions = []inbound.Addition{ + inbound.WithInName("DEFAULT-SOCKS"), + inbound.WithSpecialRules(""), + } } l, err := net.ListenPacket("udp", addr) if err != nil { diff --git a/listener/tproxy/tproxy.go b/listener/tproxy/tproxy.go index 59b8f090..198481f7 100644 --- a/listener/tproxy/tproxy.go +++ b/listener/tproxy/tproxy.go @@ -38,10 +38,10 @@ func (l *Listener) handleTProxy(conn net.Conn, in chan<- C.ConnContext, addition func New(addr string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) { if len(additions) == 0 { - additions = []inbound.Addition{{ - InName: "DEFAULT-TPROXY", - SpecialRules: "", - }} + additions = []inbound.Addition{ + inbound.WithInName("DEFAULT-TPROXY"), + inbound.WithSpecialRules(""), + } } l, err := net.Listen("tcp", addr) if err != nil { diff --git a/listener/tproxy/udp.go b/listener/tproxy/udp.go index 9488a668..f85c9ea9 100644 --- a/listener/tproxy/udp.go +++ b/listener/tproxy/udp.go @@ -34,10 +34,10 @@ func (l *UDPListener) Close() error { func NewUDP(addr string, in chan<- C.PacketAdapter, additions ...inbound.Addition) (*UDPListener, error) { if len(additions) == 0 { - additions = []inbound.Addition{{ - InName: "DEFAULT-TPROXY", - SpecialRules: "", - }} + additions = []inbound.Addition{ + inbound.WithInName("DEFAULT-TPROXY"), + inbound.WithSpecialRules(""), + } } l, err := net.ListenPacket("udp", addr) if err != nil { diff --git a/listener/tuic/server.go b/listener/tuic/server.go index a5ed7151..8b928637 100644 --- a/listener/tuic/server.go +++ b/listener/tuic/server.go @@ -27,10 +27,10 @@ type Listener struct { func New(config LC.TuicServer, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, additions ...inbound.Addition) (*Listener, error) { if len(additions) == 0 { - additions = []inbound.Addition{{ - InName: "DEFAULT-TUIC", - SpecialRules: "", - }} + additions = []inbound.Addition{ + inbound.WithInName("DEFAULT-TUIC"), + inbound.WithSpecialRules(""), + } } cert, err := CN.ParseCert(config.Certificate, config.PrivateKey) if err != nil { From a0a2eb210626e83350abc9372fae0c2da4295762 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 5 Dec 2022 11:03:28 +0800 Subject: [PATCH 200/250] chore: update dependencies --- adapter/outbound/shadowsocks.go | 4 ++-- go.mod | 12 ++++-------- go.sum | 16 ++++++++-------- listener/sing_shadowsocks/server.go | 6 +++--- listener/sing_tun/server.go | 2 +- listener/sing_tun/server_android.go | 2 +- listener/sing_tun/server_notandroid.go | 2 +- listener/sing_tun/server_notwindows.go | 2 +- listener/sing_tun/server_windows.go | 2 +- test/go.mod | 8 ++++---- test/go.sum | 16 ++++++++-------- 11 files changed, 34 insertions(+), 38 deletions(-) diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index f8f9de48..39c02b91 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -14,8 +14,8 @@ import ( "github.com/Dreamacro/clash/transport/socks5" v2rayObfs "github.com/Dreamacro/clash/transport/v2ray-plugin" - "github.com/sagernet/sing-shadowsocks" - "github.com/sagernet/sing-shadowsocks/shadowimpl" + "github.com/metacubex/sing-shadowsocks" + "github.com/metacubex/sing-shadowsocks/shadowimpl" "github.com/sagernet/sing/common/bufio" M "github.com/sagernet/sing/common/metadata" "github.com/sagernet/sing/common/uot" diff --git a/go.mod b/go.mod index 9502b144..dad90614 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/go-chi/chi/v5 v5.0.7 github.com/go-chi/cors v1.2.1 github.com/go-chi/render v1.0.2 - github.com/gofrs/uuid v4.3.0+incompatible + github.com/gofrs/uuid v4.3.1+incompatible github.com/google/gopacket v1.1.19 github.com/gorilla/websocket v1.5.0 github.com/hashicorp/golang-lru v0.5.4 @@ -20,14 +20,14 @@ require ( github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 github.com/mdlayher/netlink v1.7.0 github.com/metacubex/quic-go v0.31.1-0.20221127023445-9f0ce65a734e + github.com/metacubex/sing-shadowsocks v0.1.0 + github.com/metacubex/sing-tun v0.1.0 github.com/metacubex/sing-wireguard v0.0.0-20221109114053-16c22adda03c github.com/miekg/dns v1.1.50 github.com/oschwald/geoip2-golang v1.8.0 github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 github.com/sagernet/sing v0.1.0 - github.com/sagernet/sing-shadowsocks v0.0.0-20221112030934-e55284e180ea - github.com/sagernet/sing-tun v0.0.0-20221104121441-66c48a57776f - github.com/sagernet/sing-vmess v0.0.0-20221109021549-b446d5bdddf0 + github.com/sagernet/sing-vmess v0.1.0 github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c github.com/samber/lo v1.35.0 github.com/sirupsen/logrus v1.9.0 @@ -46,10 +46,6 @@ require ( lukechampine.com/blake3 v1.1.7 ) -replace github.com/sagernet/sing-tun => github.com/MetaCubeX/sing-tun v0.0.0-20221127052020-dbf40f3160d8 - -replace github.com/sagernet/sing-shadowsocks => github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221116103607-48a7095182b1 - require ( github.com/ajg/form v1.5.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/go.sum b/go.sum index 9fb2c366..119e36f8 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,3 @@ -github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221116103607-48a7095182b1 h1:guMcwJSIjk+jg/38uMPK5hIWVSaLHJ/l+ABZ8w2CKm0= -github.com/MetaCubeX/sing-shadowsocks v0.0.0-20221116103607-48a7095182b1/go.mod h1:3bW+hVWFXOxRC1HL6CO6QHkegqjLohErGbcvt6dUN18= -github.com/MetaCubeX/sing-tun v0.0.0-20221127052020-dbf40f3160d8 h1:YufhIKD2ecIFv+UZ8yTnmjYhwnrLkIGBssPgAfNmqvE= -github.com/MetaCubeX/sing-tun v0.0.0-20221127052020-dbf40f3160d8/go.mod h1:btUIxuI5vfzUcEDFVrG48aHM2stzg4jcI7mFQeEsei4= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY= github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= @@ -32,8 +28,8 @@ github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg= github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/gofrs/uuid v4.3.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc= -github.com/gofrs/uuid v4.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI= +github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= @@ -96,6 +92,10 @@ github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= github.com/metacubex/quic-go v0.31.1-0.20221127023445-9f0ce65a734e h1:RnfC6+sShJ3biU2Q2wuh4FxZ8/3fp1QG+1zAfswVehA= github.com/metacubex/quic-go v0.31.1-0.20221127023445-9f0ce65a734e/go.mod h1:7NPWVTLiX2Ss9q9gBNZaNHsPqZ3Tg/ApyrXxxUYbl78= +github.com/metacubex/sing-shadowsocks v0.1.0 h1:uGBtNkpy4QFlofaNkJf+iFegeLU11VzTUlkC46FHF8A= +github.com/metacubex/sing-shadowsocks v0.1.0/go.mod h1:8pBSYDKVxTtqUtGZyEh4ZpFJXwP6wBVVKrs6oQiOwmQ= +github.com/metacubex/sing-tun v0.1.0 h1:iQj0+0WjJynSKAtfv87wOZlVKWl3w9RvkOSkVe9zuMg= +github.com/metacubex/sing-tun v0.1.0/go.mod h1:l4JyI6RTrlHLQz5vSakg+wxA+LwGVI0Mz5ZtlOv67dA= github.com/metacubex/sing-wireguard v0.0.0-20221109114053-16c22adda03c h1:VHtXDny/TNOF7YDT9d9Qkr+x6K1O4cejXLlyPUXDeXQ= github.com/metacubex/sing-wireguard v0.0.0-20221109114053-16c22adda03c/go.mod h1:fULJ451x1/XlpIhl+Oo+EPGKla9tFZaqT5dKLrZ+NvM= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= @@ -121,8 +121,8 @@ github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJ github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.1.0 h1:FGmaP2BVPYO2IyC/3R1DaQa/zr+kOKHRgWqrmOF+Gu8= github.com/sagernet/sing v0.1.0/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4= -github.com/sagernet/sing-vmess v0.0.0-20221109021549-b446d5bdddf0 h1:z3kuD3hPNdEq7/wVy5lwE21f+8ZTazBtR81qswxJoCc= -github.com/sagernet/sing-vmess v0.0.0-20221109021549-b446d5bdddf0/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM= +github.com/sagernet/sing-vmess v0.1.0 h1:x0tYBJRbVi7zVXpMEW45eApGpXIDs9ub3raglouAKMo= +github.com/sagernet/sing-vmess v0.1.0/go.mod h1:4lwj6EHrUlgRnKhbmtboGbt+wtl5+tHMv96Ez8LZArw= github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c h1:qP3ZOHnjZalvqbjundbXiv/YrNlo3HOgrKc+S1QGs0U= github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI= github.com/samber/lo v1.35.0 h1:GlT8CV1GE+v97Y7MLF1wXvX6mjoxZ+hi61tj/ZcQwY0= diff --git a/listener/sing_shadowsocks/server.go b/listener/sing_shadowsocks/server.go index d89085cd..305a9496 100644 --- a/listener/sing_shadowsocks/server.go +++ b/listener/sing_shadowsocks/server.go @@ -14,9 +14,9 @@ import ( "github.com/Dreamacro/clash/listener/sing" "github.com/Dreamacro/clash/log" - shadowsocks "github.com/sagernet/sing-shadowsocks" - "github.com/sagernet/sing-shadowsocks/shadowaead" - "github.com/sagernet/sing-shadowsocks/shadowaead_2022" + shadowsocks "github.com/metacubex/sing-shadowsocks" + "github.com/metacubex/sing-shadowsocks/shadowaead" + "github.com/metacubex/sing-shadowsocks/shadowaead_2022" "github.com/sagernet/sing/common" "github.com/sagernet/sing/common/buf" "github.com/sagernet/sing/common/bufio" diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index bebff736..0dc4d476 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -15,7 +15,7 @@ import ( "github.com/Dreamacro/clash/listener/sing" "github.com/Dreamacro/clash/log" - tun "github.com/sagernet/sing-tun" + tun "github.com/metacubex/sing-tun" "github.com/sagernet/sing/common" E "github.com/sagernet/sing/common/exceptions" F "github.com/sagernet/sing/common/format" diff --git a/listener/sing_tun/server_android.go b/listener/sing_tun/server_android.go index f8befe6e..4f85c418 100644 --- a/listener/sing_tun/server_android.go +++ b/listener/sing_tun/server_android.go @@ -2,8 +2,8 @@ package sing_tun import ( "github.com/Dreamacro/clash/log" + tun "github.com/metacubex/sing-tun" "github.com/sagernet/netlink" - tun "github.com/sagernet/sing-tun" "golang.org/x/sys/unix" "runtime" ) diff --git a/listener/sing_tun/server_notandroid.go b/listener/sing_tun/server_notandroid.go index 6db98989..6b30ee03 100644 --- a/listener/sing_tun/server_notandroid.go +++ b/listener/sing_tun/server_notandroid.go @@ -3,7 +3,7 @@ package sing_tun import ( - tun "github.com/sagernet/sing-tun" + tun "github.com/metacubex/sing-tun" ) func (l *Listener) buildAndroidRules(tunOptions *tun.Options) error { diff --git a/listener/sing_tun/server_notwindows.go b/listener/sing_tun/server_notwindows.go index 27872d8e..d3280c5c 100644 --- a/listener/sing_tun/server_notwindows.go +++ b/listener/sing_tun/server_notwindows.go @@ -3,7 +3,7 @@ package sing_tun import ( - tun "github.com/sagernet/sing-tun" + tun "github.com/metacubex/sing-tun" ) func tunOpen(options tun.Options) (tun.Tun, error) { diff --git a/listener/sing_tun/server_windows.go b/listener/sing_tun/server_windows.go index 20fc7031..7b745cac 100644 --- a/listener/sing_tun/server_windows.go +++ b/listener/sing_tun/server_windows.go @@ -5,7 +5,7 @@ import ( "github.com/Dreamacro/clash/log" - tun "github.com/sagernet/sing-tun" + tun "github.com/metacubex/sing-tun" ) func tunOpen(options tun.Options) (tunIf tun.Tun, err error) { diff --git a/test/go.mod b/test/go.mod index 8be96364..6cbf50e8 100644 --- a/test/go.mod +++ b/test/go.mod @@ -25,7 +25,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect - github.com/gofrs/uuid v4.3.0+incompatible // indirect + github.com/gofrs/uuid v4.3.1+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/mock v1.6.0 // indirect github.com/google/btree v1.0.1 // indirect @@ -44,6 +44,8 @@ require ( github.com/mdlayher/netlink v1.7.0 // indirect github.com/mdlayher/socket v0.4.0 // indirect github.com/metacubex/quic-go v0.31.1-0.20221127023445-9f0ce65a734e // indirect + github.com/metacubex/sing-shadowsocks v0.1.0 // indirect + github.com/metacubex/sing-tun v0.1.0 // indirect github.com/metacubex/sing-wireguard v0.0.0-20221109114053-16c22adda03c // indirect github.com/moby/term v0.0.0-20221105221325-4eb28fa6025c // indirect github.com/morikuni/aec v1.0.0 // indirect @@ -58,9 +60,7 @@ require ( 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.1.0 // indirect - github.com/sagernet/sing-shadowsocks v0.0.0-20221112030934-e55284e180ea // indirect - github.com/sagernet/sing-tun v0.0.0-20221104121441-66c48a57776f // indirect - github.com/sagernet/sing-vmess v0.0.0-20221109021549-b446d5bdddf0 // indirect + github.com/sagernet/sing-vmess v0.1.0 // indirect github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c // indirect github.com/samber/lo v1.35.0 // indirect github.com/sirupsen/logrus v1.9.0 // indirect diff --git a/test/go.sum b/test/go.sum index 96d6f3ab..22e24a16 100644 --- a/test/go.sum +++ b/test/go.sum @@ -31,8 +31,8 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/gofrs/uuid v4.3.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc= -github.com/gofrs/uuid v4.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.3.1+incompatible h1:0/KbAdpx3UXAx1kEOWHJeOkpbgRFGHVgv+CFIY7dBJI= +github.com/gofrs/uuid v4.3.1+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= @@ -97,6 +97,10 @@ github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= github.com/metacubex/quic-go v0.31.1-0.20221127023445-9f0ce65a734e h1:RnfC6+sShJ3biU2Q2wuh4FxZ8/3fp1QG+1zAfswVehA= github.com/metacubex/quic-go v0.31.1-0.20221127023445-9f0ce65a734e/go.mod h1:7NPWVTLiX2Ss9q9gBNZaNHsPqZ3Tg/ApyrXxxUYbl78= +github.com/metacubex/sing-shadowsocks v0.1.0 h1:uGBtNkpy4QFlofaNkJf+iFegeLU11VzTUlkC46FHF8A= +github.com/metacubex/sing-shadowsocks v0.1.0/go.mod h1:8pBSYDKVxTtqUtGZyEh4ZpFJXwP6wBVVKrs6oQiOwmQ= +github.com/metacubex/sing-tun v0.1.0 h1:iQj0+0WjJynSKAtfv87wOZlVKWl3w9RvkOSkVe9zuMg= +github.com/metacubex/sing-tun v0.1.0/go.mod h1:l4JyI6RTrlHLQz5vSakg+wxA+LwGVI0Mz5ZtlOv67dA= github.com/metacubex/sing-wireguard v0.0.0-20221109114053-16c22adda03c h1:VHtXDny/TNOF7YDT9d9Qkr+x6K1O4cejXLlyPUXDeXQ= github.com/metacubex/sing-wireguard v0.0.0-20221109114053-16c22adda03c/go.mod h1:fULJ451x1/XlpIhl+Oo+EPGKla9tFZaqT5dKLrZ+NvM= github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= @@ -131,12 +135,8 @@ github.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJ github.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY= github.com/sagernet/sing v0.1.0 h1:FGmaP2BVPYO2IyC/3R1DaQa/zr+kOKHRgWqrmOF+Gu8= github.com/sagernet/sing v0.1.0/go.mod h1:zvgDYKI+vCAW9RyfyrKTgleI+DOa8lzHMPC7VZo3OL4= -github.com/sagernet/sing-shadowsocks v0.0.0-20221112030934-e55284e180ea h1:OO+oV7XNQ7eah2bQyT2pSAoeCBHpbAU3cVVbGd7TE/g= -github.com/sagernet/sing-shadowsocks v0.0.0-20221112030934-e55284e180ea/go.mod h1:16sNARQbsFbYIzAuPySszQA6Wfgzk7GWSzh1a6kDrUU= -github.com/sagernet/sing-tun v0.0.0-20221104121441-66c48a57776f h1:CXF+nErOb9f7qiHingSgTa2/lJAgmEFtAQ47oVwdRGU= -github.com/sagernet/sing-tun v0.0.0-20221104121441-66c48a57776f/go.mod h1:1u3pjXA9HmH7kRiBJqM3C/zPxrxnCLd3svmqtub/RFU= -github.com/sagernet/sing-vmess v0.0.0-20221109021549-b446d5bdddf0 h1:z3kuD3hPNdEq7/wVy5lwE21f+8ZTazBtR81qswxJoCc= -github.com/sagernet/sing-vmess v0.0.0-20221109021549-b446d5bdddf0/go.mod h1:bwhAdSNET1X+j9DOXGj9NIQR39xgcWIk1rOQ9lLD+gM= +github.com/sagernet/sing-vmess v0.1.0 h1:x0tYBJRbVi7zVXpMEW45eApGpXIDs9ub3raglouAKMo= +github.com/sagernet/sing-vmess v0.1.0/go.mod h1:4lwj6EHrUlgRnKhbmtboGbt+wtl5+tHMv96Ez8LZArw= github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c h1:qP3ZOHnjZalvqbjundbXiv/YrNlo3HOgrKc+S1QGs0U= github.com/sagernet/wireguard-go v0.0.0-20221108054404-7c2acadba17c/go.mod h1:euOmN6O5kk9dQmgSS8Df4psAl3TCjxOz0NW60EWkSaI= github.com/samber/lo v1.35.0 h1:GlT8CV1GE+v97Y7MLF1wXvX6mjoxZ+hi61tj/ZcQwY0= From 8c58d8a8ad5feb8f9db93d0fe7ef6f3eee6f2b2c Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 5 Dec 2022 16:29:50 +0800 Subject: [PATCH 201/250] fix: remove unneed listener's start --- listener/listener.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/listener/listener.go b/listener/listener.go index 99e16aea..0530e20e 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -267,7 +267,7 @@ func ReCreateShadowSocks(shadowSocksConfig string, tcpIn chan<- C.ConnContext, u var ssConfig LC.ShadowsocksServer if addr, cipher, password, err := embedSS.ParseSSURL(shadowSocksConfig); err == nil { ssConfig = LC.ShadowsocksServer{ - Enable: true, + Enable: len(shadowSocksConfig) > 0, Listen: addr, Password: password, Cipher: cipher, @@ -320,7 +320,7 @@ func ReCreateVmess(vmessConfig string, tcpIn chan<- C.ConnContext, udpIn chan<- var vsConfig LC.VmessServer if addr, username, password, err := sing_vmess.ParseVmessURL(vmessConfig); err == nil { vsConfig = LC.VmessServer{ - Enable: true, + Enable: len(vmessConfig) > 0, Listen: addr, Users: []LC.VmessUser{{Username: username, UUID: password, AlterID: 1}}, } From 5c410b8df453fe63415b734748579081495381a9 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 5 Dec 2022 17:03:12 +0800 Subject: [PATCH 202/250] chore: listeners support tunnel --- docs/config.yaml | 11 +++++ listener/inbound/tunnel.go | 92 ++++++++++++++++++++++++++++++++++++++ listener/parse.go | 7 +++ listener/tunnel/tcp.go | 8 ++-- listener/tunnel/udp.go | 8 ++-- 5 files changed, 118 insertions(+), 8 deletions(-) create mode 100644 listener/inbound/tunnel.go diff --git a/docs/config.yaml b/docs/config.yaml index c20228db..1df28909 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -671,11 +671,13 @@ listeners: #listen: 0.0.0.0 # 默认监听 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules # udp: false # 默认 true + - name: http-in-1 type: http port: 10809 listen: 0.0.0.0 # rule: sub-rule + - name: mixed-in-1 type: mixed # HTTP(S) 和 SOCKS 代理混合 port: 10810 @@ -729,3 +731,12 @@ listeners: # alpn: # - h3 # max-udp-relay-packet-size: 1500 + + - name: tunnel-in-1 + type: tunnel + port: 10816 + listen: 0.0.0.0 + # rule: sub-rule + network: [ tcp, udp ] + target: target.com + diff --git a/listener/inbound/tunnel.go b/listener/inbound/tunnel.go new file mode 100644 index 00000000..683f58c9 --- /dev/null +++ b/listener/inbound/tunnel.go @@ -0,0 +1,92 @@ +package inbound + +import ( + "fmt" + + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/listener/tunnel" + "github.com/Dreamacro/clash/log" +) + +type TunnelOption struct { + BaseOption + Network []string `inbound:"network"` + Target string `inbound:"target"` + Proxy string `inbound:"proxy,omitempty"` +} + +func (o TunnelOption) Equal(config C.InboundConfig) bool { + return optionToString(o) == optionToString(config) +} + +type Tunnel struct { + *Base + config *TunnelOption + ttl *tunnel.Listener + tul *tunnel.PacketConn +} + +func NewTunnel(options *TunnelOption) (*Tunnel, error) { + base, err := NewBase(&options.BaseOption) + if err != nil { + return nil, err + } + return &Tunnel{ + Base: base, + config: options, + }, nil +} + +// Config implements constant.InboundListener +func (t *Tunnel) Config() C.InboundConfig { + return t.config +} + +// Close implements constant.InboundListener +func (t *Tunnel) Close() error { + var err error + if t.ttl != nil { + if tcpErr := t.ttl.Close(); tcpErr != nil { + err = tcpErr + } + } + if t.tul != nil { + if udpErr := t.tul.Close(); udpErr != nil { + if err == nil { + err = udpErr + } else { + return fmt.Errorf("close tcp err: %t, close udp err: %t", err.Error(), udpErr.Error()) + } + } + } + + return err +} + +// Address implements constant.InboundListener +func (t *Tunnel) Address() string { + return t.ttl.Address() +} + +// Listen implements constant.InboundListener +func (t *Tunnel) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { + var err error + for _, network := range t.config.Network { + switch network { + case "tcp": + if t.ttl, err = tunnel.New(t.RawAddress(), t.config.Target, t.config.Proxy, tcpIn, t.Additions()...); err != nil { + return err + } + case "udp": + if t.tul, err = tunnel.NewUDP(t.RawAddress(), t.config.Target, t.config.Proxy, udpIn, t.Additions()...); err != nil { + return err + } + default: + return fmt.Errorf("unknow network type: %s", network) + } + log.Infoln("Tunnel[%s](%s/%s)proxy listening at: %s", t.Name(), network, t.config.Target, t.Address()) + } + return nil +} + +var _ C.InboundListener = (*Tunnel)(nil) diff --git a/listener/parse.go b/listener/parse.go index eef1df16..deb4e10d 100644 --- a/listener/parse.go +++ b/listener/parse.go @@ -55,6 +55,13 @@ func ParseListener(mapping map[string]any) (C.InboundListener, error) { return nil, err } listener, err = IN.NewMixed(mixedOption) + case "tunnel": + tunnelOption := &IN.TunnelOption{} + err = decoder.Decode(mapping, tunnelOption) + if err != nil { + return nil, err + } + listener, err = IN.NewTunnel(tunnelOption) case "shadowsocks": shadowsocksOption := &IN.ShadowSocksOption{} err = decoder.Decode(mapping, shadowsocksOption) diff --git a/listener/tunnel/tcp.go b/listener/tunnel/tcp.go index 4ae5865c..bf278c1c 100644 --- a/listener/tunnel/tcp.go +++ b/listener/tunnel/tcp.go @@ -33,14 +33,14 @@ func (l *Listener) Close() error { return l.listener.Close() } -func (l *Listener) handleTCP(conn net.Conn, in chan<- C.ConnContext) { +func (l *Listener) handleTCP(conn net.Conn, in chan<- C.ConnContext, additions ...inbound.Addition) { conn.(*net.TCPConn).SetKeepAlive(true) - ctx := inbound.NewSocket(l.target, conn, C.TUNNEL) + ctx := inbound.NewSocket(l.target, conn, C.TUNNEL, additions...) ctx.Metadata().SpecialProxy = l.proxy in <- ctx } -func New(addr, target, proxy string, in chan<- C.ConnContext) (*Listener, error) { +func New(addr, target, proxy string, in chan<- C.ConnContext, additions ...inbound.Addition) (*Listener, error) { l, err := net.Listen("tcp", addr) if err != nil { return nil, err @@ -67,7 +67,7 @@ func New(addr, target, proxy string, in chan<- C.ConnContext) (*Listener, error) } continue } - go rl.handleTCP(c, in) + go rl.handleTCP(c, in, additions...) } }() diff --git a/listener/tunnel/udp.go b/listener/tunnel/udp.go index 506e990f..0795084c 100644 --- a/listener/tunnel/udp.go +++ b/listener/tunnel/udp.go @@ -34,7 +34,7 @@ func (l *PacketConn) Close() error { return l.conn.Close() } -func NewUDP(addr, target, proxy string, in chan<- C.PacketAdapter) (*PacketConn, error) { +func NewUDP(addr, target, proxy string, in chan<- C.PacketAdapter, additions ...inbound.Addition) (*PacketConn, error) { l, err := net.ListenPacket("udp", addr) if err != nil { return nil, err @@ -62,21 +62,21 @@ func NewUDP(addr, target, proxy string, in chan<- C.PacketAdapter) (*PacketConn, } continue } - sl.handleUDP(l, in, buf[:n], remoteAddr) + sl.handleUDP(l, in, buf[:n], remoteAddr, additions...) } }() return sl, nil } -func (l *PacketConn) handleUDP(pc net.PacketConn, in chan<- C.PacketAdapter, buf []byte, addr net.Addr) { +func (l *PacketConn) handleUDP(pc net.PacketConn, in chan<- C.PacketAdapter, buf []byte, addr net.Addr, additions ...inbound.Addition) { packet := &packet{ pc: pc, rAddr: addr, payload: buf, } - ctx := inbound.NewPacket(l.target, packet, C.TUNNEL) + ctx := inbound.NewPacket(l.target, packet, C.TUNNEL, additions...) ctx.Metadata().SpecialProxy = l.proxy select { case in <- ctx: From fd9c4cbfa51519f78d2d96bb5831f74848620875 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 5 Dec 2022 17:43:50 +0800 Subject: [PATCH 203/250] chore: listeners support tun --- config/config.go | 2 +- docs/config.yaml | 37 +++++++++ listener/config/tun.go | 12 +++ listener/inbound/base.go | 2 +- listener/inbound/shadowsocks.go | 20 ++--- listener/inbound/tuic.go | 32 ++++---- listener/inbound/tun.go | 139 ++++++++++++++++++++++++++++++++ listener/inbound/tunnel.go | 5 +- listener/inbound/vmess.go | 26 +++--- listener/listener.go | 1 + listener/parse.go | 10 +++ listener/sing_tun/server.go | 24 ++++-- 12 files changed, 260 insertions(+), 50 deletions(-) create mode 100644 listener/inbound/tun.go diff --git a/config/config.go b/config/config.go index 0dc100f4..b5bc6a1d 100644 --- a/config/config.go +++ b/config/config.go @@ -243,7 +243,7 @@ type RawTun struct { RedirectToTun []string `yaml:"-" json:"-"` MTU uint32 `yaml:"mtu" json:"mtu,omitempty"` - //Inet4Address []ListenPrefix `yaml:"inet4-address" json:"inet4_address,omitempty"` + //Inet4Address []LC.ListenPrefix `yaml:"inet4-address" json:"inet4_address,omitempty"` Inet6Address []LC.ListenPrefix `yaml:"inet6-address" json:"inet6_address,omitempty"` StrictRoute bool `yaml:"strict-route" json:"strict_route,omitempty"` Inet4RouteAddress []LC.ListenPrefix `yaml:"inet4_route_address" json:"inet4_route_address,omitempty"` diff --git a/docs/config.yaml b/docs/config.yaml index 1df28909..161046ad 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -740,3 +740,40 @@ listeners: network: [ tcp, udp ] target: target.com + - name: tun-in-1 + type: tun + # rule: sub-rule + stack: system # gvisor / lwip + dns-hijack: + - 0.0.0.0:53 # 需要劫持的 DNS + # auto-detect-interface: false # 自动识别出口网卡 + # auto-route: false # 配置路由表 + # mtu: 9000 # 最大传输单元 + # strict_route: true # 将所有连接路由到tun来防止泄漏,但你的设备将无法其他设备被访问 + inet4_route_address: # 启用 auto_route 时使用自定义路由而不是默认路由 + - 0.0.0.0/1 + - 128.0.0.0/1 + inet6_route_address: # 启用 auto_route 时使用自定义路由而不是默认路由 + - "::/1" + - "8000::/1" + # endpoint_independent_nat: false # 启用独立于端点的 NAT + # include_uid: # UID 规则仅在 Linux 下被支持,并且需要 auto_route + # - 0 + # include_uid_range: # 限制被路由的的用户范围 + # - 1000-99999 + # exclude_uid: # 排除路由的的用户 + #- 1000 + # exclude_uid_range: # 排除路由的的用户范围 + # - 1000-99999 + + # Android 用户和应用规则仅在 Android 下被支持 + # 并且需要 auto_route + + # include_android_user: # 限制被路由的 Android 用户 + # - 0 + # - 10 + # include_package: # 限制被路由的 Android 应用包名 + # - com.android.chrome + # exclude_package: # 排除被路由的 Android 应用包名 + # - com.android.captiveportallogin + diff --git a/listener/config/tun.go b/listener/config/tun.go index 828ed488..e7b2c930 100644 --- a/listener/config/tun.go +++ b/listener/config/tun.go @@ -58,6 +58,18 @@ func (p ListenPrefix) Build() netip.Prefix { return netip.Prefix(p) } +func StringSliceToListenPrefixSlice(ss []string) ([]ListenPrefix, error) { + lps := make([]ListenPrefix, 0, len(ss)) + for _, s := range ss { + prefix, err := netip.ParsePrefix(s) + if err != nil { + return nil, err + } + lps = append(lps, ListenPrefix(prefix)) + } + return lps, nil +} + type Tun struct { Enable bool Device string diff --git a/listener/inbound/base.go b/listener/inbound/base.go index 46cd190d..39366e1b 100644 --- a/listener/inbound/base.go +++ b/listener/inbound/base.go @@ -74,7 +74,7 @@ var _ C.InboundListener = (*Base)(nil) type BaseOption struct { NameStr string `inbound:"name"` Listen string `inbound:"listen,omitempty"` - Port int `inbound:"port"` + Port int `inbound:"port,omitempty"` SpecialRules string `inbound:"rule,omitempty"` } diff --git a/listener/inbound/shadowsocks.go b/listener/inbound/shadowsocks.go index c21f2f39..e6baa80c 100644 --- a/listener/inbound/shadowsocks.go +++ b/listener/inbound/shadowsocks.go @@ -21,6 +21,7 @@ type ShadowSocks struct { *Base config *ShadowSocksOption l C.MultiAddrListener + ss LC.ShadowsocksServer } func NewShadowSocks(options *ShadowSocksOption) (*ShadowSocks, error) { @@ -31,8 +32,13 @@ func NewShadowSocks(options *ShadowSocksOption) (*ShadowSocks, error) { return &ShadowSocks{ Base: base, config: options, + ss: LC.ShadowsocksServer{ + Enable: true, + Listen: base.RawAddress(), + Password: options.Password, + Cipher: options.Cipher, + }, }, nil - } // Config implements constant.InboundListener @@ -53,17 +59,7 @@ func (s *ShadowSocks) Address() string { // Listen implements constant.InboundListener func (s *ShadowSocks) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error - s.l, err = sing_shadowsocks.New( - LC.ShadowsocksServer{ - Enable: true, - Listen: s.RawAddress(), - Password: s.config.Password, - Cipher: s.config.Cipher, - }, - tcpIn, - udpIn, - s.Additions()..., - ) + s.l, err = sing_shadowsocks.New(s.ss, tcpIn, udpIn, s.Additions()...) if err != nil { return err } diff --git a/listener/inbound/tuic.go b/listener/inbound/tuic.go index efe5d8b2..f3b8a5f2 100644 --- a/listener/inbound/tuic.go +++ b/listener/inbound/tuic.go @@ -27,6 +27,7 @@ type Tuic struct { *Base config *TuicOption l *tuic.Listener + ts LC.TuicServer } func NewTuic(options *TuicOption) (*Tuic, error) { @@ -37,8 +38,19 @@ func NewTuic(options *TuicOption) (*Tuic, error) { return &Tuic{ Base: base, config: options, + ts: LC.TuicServer{ + Enable: true, + Listen: base.RawAddress(), + Token: options.Token, + Certificate: options.Certificate, + PrivateKey: options.PrivateKey, + CongestionController: options.CongestionController, + MaxIdleTime: options.MaxIdleTime, + AuthenticationTimeout: options.AuthenticationTimeout, + ALPN: options.ALPN, + MaxUdpRelayPacketSize: options.MaxUdpRelayPacketSize, + }, }, nil - } // Config implements constant.InboundListener @@ -59,23 +71,7 @@ func (t *Tuic) Address() string { // Listen implements constant.InboundListener func (t *Tuic) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error - t.l, err = tuic.New( - LC.TuicServer{ - Enable: true, - Listen: t.RawAddress(), - Token: t.config.Token, - Certificate: t.config.Certificate, - PrivateKey: t.config.PrivateKey, - CongestionController: t.config.CongestionController, - MaxIdleTime: t.config.MaxIdleTime, - AuthenticationTimeout: t.config.AuthenticationTimeout, - ALPN: t.config.ALPN, - MaxUdpRelayPacketSize: t.config.MaxUdpRelayPacketSize, - }, - tcpIn, - udpIn, - t.Additions()..., - ) + t.l, err = tuic.New(t.ts, tcpIn, udpIn, t.Additions()...) if err != nil { return err } diff --git a/listener/inbound/tun.go b/listener/inbound/tun.go new file mode 100644 index 00000000..3365ed89 --- /dev/null +++ b/listener/inbound/tun.go @@ -0,0 +1,139 @@ +package inbound + +import ( + "errors" + "net/netip" + "strings" + + C "github.com/Dreamacro/clash/constant" + LC "github.com/Dreamacro/clash/listener/config" + "github.com/Dreamacro/clash/listener/sing_tun" + "github.com/Dreamacro/clash/log" +) + +type TunOption struct { + BaseOption + Device string `inbound:"device,omitempty"` + Stack string `inbound:"stack,omitempty"` + DNSHijack []string `inbound:"dns-hijack,omitempty"` + AutoRoute bool `inbound:"auto-route,omitempty"` + AutoDetectInterface bool `inbound:"auto-detect-interface,omitempty"` + + MTU uint32 `inbound:"mtu,omitempty"` + Inet4Address []string `inbound:"inet4_address,omitempty"` + Inet6Address []string `inbound:"inet6_address,omitempty"` + StrictRoute bool `inbound:"strict_route,omitempty"` + Inet4RouteAddress []string `inbound:"inet4_route_address,omitempty"` + Inet6RouteAddress []string `inbound:"inet6_route_address,omitempty"` + IncludeUID []uint32 `inbound:"include_uid,omitempty"` + IncludeUIDRange []string `inbound:"include_uid_range,omitempty"` + ExcludeUID []uint32 `inbound:"exclude_uid,omitempty"` + ExcludeUIDRange []string `inbound:"exclude_uid_range,omitempty"` + IncludeAndroidUser []int `inbound:"include_android_user,omitempty"` + IncludePackage []string `inbound:"include_package,omitempty"` + ExcludePackage []string `inbound:"exclude_package,omitempty"` + EndpointIndependentNat bool `inbound:"endpoint_independent_nat,omitempty"` + UDPTimeout int64 `inbound:"udp_timeout,omitempty"` +} + +func (o TunOption) Equal(config C.InboundConfig) bool { + return optionToString(o) == optionToString(config) +} + +type Tun struct { + *Base + config *TunOption + l *sing_tun.Listener + tun LC.Tun +} + +func NewTun(options *TunOption) (*Tun, error) { + base, err := NewBase(&options.BaseOption) + if err != nil { + return nil, err + } + stack, exist := C.StackTypeMapping[strings.ToLower(options.Stack)] + if !exist { + return nil, errors.New("invalid tun stack") + } + dnsHijack := make([]netip.AddrPort, 0, len(options.DNSHijack)) + for _, str := range options.DNSHijack { + var a netip.AddrPort + err = a.UnmarshalText([]byte(str)) + if err != nil { + return nil, err + } + dnsHijack = append(dnsHijack, a) + } + inet4Address, err := LC.StringSliceToListenPrefixSlice(options.Inet4Address) + if err != nil { + return nil, err + } + inet6Address, err := LC.StringSliceToListenPrefixSlice(options.Inet6Address) + if err != nil { + return nil, err + } + inet4RouteAddress, err := LC.StringSliceToListenPrefixSlice(options.Inet4RouteAddress) + if err != nil { + return nil, err + } + inet6RouteAddress, err := LC.StringSliceToListenPrefixSlice(options.Inet6RouteAddress) + if err != nil { + return nil, err + } + return &Tun{ + Base: base, + config: options, + tun: LC.Tun{ + Enable: true, + Device: options.Device, + Stack: stack, + DNSHijack: dnsHijack, + AutoRoute: options.AutoRoute, + AutoDetectInterface: options.AutoDetectInterface, + MTU: options.MTU, + Inet4Address: inet4Address, + Inet6Address: inet6Address, + StrictRoute: options.StrictRoute, + Inet4RouteAddress: inet4RouteAddress, + Inet6RouteAddress: inet6RouteAddress, + IncludeUID: options.IncludeUID, + IncludeUIDRange: options.IncludeUIDRange, + ExcludeUID: options.ExcludeUID, + ExcludeUIDRange: options.ExcludeUIDRange, + IncludeAndroidUser: options.IncludeAndroidUser, + IncludePackage: options.IncludePackage, + ExcludePackage: options.ExcludePackage, + EndpointIndependentNat: options.EndpointIndependentNat, + UDPTimeout: options.UDPTimeout, + }, + }, nil +} + +// Config implements constant.InboundListener +func (t *Tun) Config() C.InboundConfig { + return t.config +} + +// Address implements constant.InboundListener +func (t *Tun) Address() string { + return t.l.Address() +} + +// Listen implements constant.InboundListener +func (t *Tun) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { + var err error + t.l, err = sing_tun.New(t.tun, tcpIn, udpIn, t.Additions()...) + if err != nil { + return err + } + log.Infoln("Tun[%s] proxy listening at: %s", t.Name(), t.Address()) + return nil +} + +// Close implements constant.InboundListener +func (t *Tun) Close() error { + return t.l.Close() +} + +var _ C.InboundListener = (*Tun)(nil) diff --git a/listener/inbound/tunnel.go b/listener/inbound/tunnel.go index 683f58c9..c8478e36 100644 --- a/listener/inbound/tunnel.go +++ b/listener/inbound/tunnel.go @@ -55,7 +55,7 @@ func (t *Tunnel) Close() error { if err == nil { err = udpErr } else { - return fmt.Errorf("close tcp err: %t, close udp err: %t", err.Error(), udpErr.Error()) + return fmt.Errorf("close tcp err: %s, close udp err: %s", err.Error(), udpErr.Error()) } } } @@ -82,7 +82,8 @@ func (t *Tunnel) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter return err } default: - return fmt.Errorf("unknow network type: %s", network) + log.Warnln("unknown network type: %s, passed", network) + continue } log.Infoln("Tunnel[%s](%s/%s)proxy listening at: %s", t.Name(), network, t.config.Target, t.Address()) } diff --git a/listener/inbound/vmess.go b/listener/inbound/vmess.go index 745da45e..130e17c5 100644 --- a/listener/inbound/vmess.go +++ b/listener/inbound/vmess.go @@ -26,6 +26,7 @@ type Vmess struct { *Base config *VmessOption l C.MultiAddrListener + vs LC.VmessServer } func NewVmess(options *VmessOption) (*Vmess, error) { @@ -33,11 +34,23 @@ func NewVmess(options *VmessOption) (*Vmess, error) { if err != nil { return nil, err } + users := make([]LC.VmessUser, len(options.Users)) + for i, v := range options.Users { + users[i] = LC.VmessUser{ + Username: v.Username, + UUID: v.UUID, + AlterID: v.AlterID, + } + } return &Vmess{ Base: base, config: options, + vs: LC.VmessServer{ + Enable: true, + Listen: base.RawAddress(), + Users: users, + }, }, nil - } // Config implements constant.InboundListener @@ -66,16 +79,7 @@ func (v *Vmess) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) AlterID: v.AlterID, } } - v.l, err = sing_vmess.New( - LC.VmessServer{ - Enable: true, - Listen: v.RawAddress(), - Users: users, - }, - tcpIn, - udpIn, - v.Additions()..., - ) + v.l, err = sing_vmess.New(v.vs, tcpIn, udpIn, v.Additions()...) if err != nil { return err } diff --git a/listener/listener.go b/listener/listener.go index 0530e20e..ab9963b6 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -533,6 +533,7 @@ func ReCreateTun(tunConf LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.Pack } tunLister, err = sing_tun.New(tunConf, tcpIn, udpIn) + log.Infoln("[TUN] Tun adapter listening at: %s", tunLister.Address()) } func ReCreateRedirToTun(ifaceNames []string) { diff --git a/listener/parse.go b/listener/parse.go index deb4e10d..9459b9e1 100644 --- a/listener/parse.go +++ b/listener/parse.go @@ -62,6 +62,16 @@ func ParseListener(mapping map[string]any) (C.InboundListener, error) { return nil, err } listener, err = IN.NewTunnel(tunnelOption) + case "tun": + tunOption := &IN.TunOption{ + Stack: C.TunGvisor.String(), + DNSHijack: []string{"0.0.0.0:53"}, // default hijack all dns query + } + err = decoder.Decode(mapping, tunOption) + if err != nil { + return nil, err + } + listener, err = IN.NewTun(tunOption) case "shadowsocks": shadowsocksOption := &IN.ShadowSocksOption{} err = decoder.Decode(mapping, shadowsocksOption) diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index 0dc4d476..d74324da 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -2,12 +2,14 @@ package sing_tun import ( "context" + "fmt" "net" "net/netip" "runtime" "strconv" "strings" + "github.com/Dreamacro/clash/adapter/inbound" "github.com/Dreamacro/clash/component/dialer" "github.com/Dreamacro/clash/component/iface" C "github.com/Dreamacro/clash/constant" @@ -29,6 +31,7 @@ type Listener struct { options LC.Tun handler *ListenerHandler tunName string + addrStr string tunIf tun.Tun tunStack tun.Stack @@ -64,7 +67,13 @@ func CalculateInterfaceName(name string) (tunName string) { return } -func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) (l *Listener, err error) { +func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter, additions ...inbound.Addition) (l *Listener, err error) { + if len(additions) == 0 { + additions = []inbound.Addition{ + inbound.WithInName("DEFAULT-TUN"), + inbound.WithSpecialRules(""), + } + } tunName := options.Device if tunName == "" { tunName = CalculateInterfaceName(InterfaceName) @@ -113,9 +122,10 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte handler := &ListenerHandler{ ListenerHandler: sing.ListenerHandler{ - TcpIn: tcpIn, - UdpIn: udpIn, - Type: C.TUN, + TcpIn: tcpIn, + UdpIn: udpIn, + Type: C.TUN, + Additions: additions, }, DnsAdds: dnsAdds, } @@ -211,7 +221,7 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte //l.openAndroidHotspot(tunOptions) - log.Infoln("[TUN] Tun adapter listening at: %s(%s,%s), mtu: %d, auto route: %v, ip stack: %s", + l.addrStr = fmt.Sprintf("%s(%s,%s), mtu: %d, auto route: %v, ip stack: %s", tunName, tunOptions.Inet4Address, tunOptions.Inet6Address, tunMTU, options.AutoRoute, options.Stack) return } @@ -286,3 +296,7 @@ func (l *Listener) Close() error { func (l *Listener) Config() LC.Tun { return l.options } + +func (l *Listener) Address() string { + return l.addrStr +} From 46d23d9b8621159bb33bee356568295c63c4829a Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 5 Dec 2022 17:53:54 +0800 Subject: [PATCH 204/250] chore: all listeners support specialProxy --- adapter/inbound/addition.go | 6 ++++++ docs/config.yaml | 28 +++++++++++++++++++--------- listener/inbound/base.go | 2 ++ listener/inbound/tunnel.go | 5 ++--- 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/adapter/inbound/addition.go b/adapter/inbound/addition.go index 8bf1e786..5966e784 100644 --- a/adapter/inbound/addition.go +++ b/adapter/inbound/addition.go @@ -21,3 +21,9 @@ func WithSpecialRules(specialRules string) Addition { metadata.SpecialRules = specialRules } } + +func WithSpecialProxy(specialProxy string) Addition { + return func(metadata *C.Metadata) { + metadata.SpecialProxy = specialProxy + } +} diff --git a/docs/config.yaml b/docs/config.yaml index 161046ad..0cb1ff78 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -670,39 +670,45 @@ listeners: port: 10808 #listen: 0.0.0.0 # 默认监听 0.0.0.0 # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules + # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理 # udp: false # 默认 true - name: http-in-1 type: http port: 10809 listen: 0.0.0.0 - # rule: sub-rule + # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules + # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) - name: mixed-in-1 type: mixed # HTTP(S) 和 SOCKS 代理混合 port: 10810 listen: 0.0.0.0 - # rule: sub-rule + # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules + # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) # udp: false # 默认 true - name: reidr-in-1 type: redir port: 10811 listen: 0.0.0.0 - # rule: sub-rule + # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules + # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) - name: tproxy-in-1 type: tproxy port: 10812 listen: 0.0.0.0 - # rule: sub-rule + # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules + # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) # udp: false # 默认 true - name: shadowsocks-in-1 type: shadowsocks port: 10813 listen: 0.0.0.0 - # rule: sub-rule + # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules + # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) password: vlmpIPSyHH6f4S8WVPdRIHIlzmB+GIRfoH3aNJ/t9Gg= cipher: 2022-blake3-aes-256-gcm @@ -710,7 +716,8 @@ listeners: type: vmess port: 10814 listen: 0.0.0.0 - # rule: sub-rule + # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules + # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) users: - username: 1 uuid: 9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68 @@ -720,7 +727,8 @@ listeners: type: tuic port: 10815 listen: 0.0.0.0 - # rule: sub-rule + # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules + # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) # token: # - TOKEN # certificate: ./server.crt @@ -736,13 +744,15 @@ listeners: type: tunnel port: 10816 listen: 0.0.0.0 - # rule: sub-rule + # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules + # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) network: [ tcp, udp ] target: target.com - name: tun-in-1 type: tun - # rule: sub-rule + # rule: sub-rule-name1 # 默认使用 rules,如果未找到 sub-rule 则直接使用 rules + # proxy: proxy # 如果不为空则直接将该入站流量交由指定proxy处理(当proxy不为空时,这里的proxy名称必须合法,否则会出错) stack: system # gvisor / lwip dns-hijack: - 0.0.0.0:53 # 需要劫持的 DNS diff --git a/listener/inbound/base.go b/listener/inbound/base.go index 39366e1b..41be5b10 100644 --- a/listener/inbound/base.go +++ b/listener/inbound/base.go @@ -76,6 +76,7 @@ type BaseOption struct { Listen string `inbound:"listen,omitempty"` Port int `inbound:"port,omitempty"` SpecialRules string `inbound:"rule,omitempty"` + SpecialProxy string `inbound:"proxy,omitempty"` } func (o BaseOption) Name() string { @@ -90,6 +91,7 @@ func (o BaseOption) Additions() []inbound.Addition { return []inbound.Addition{ inbound.WithInName(o.NameStr), inbound.WithSpecialRules(o.SpecialRules), + inbound.WithSpecialProxy(o.SpecialProxy), } } diff --git a/listener/inbound/tunnel.go b/listener/inbound/tunnel.go index c8478e36..e3e16281 100644 --- a/listener/inbound/tunnel.go +++ b/listener/inbound/tunnel.go @@ -12,7 +12,6 @@ type TunnelOption struct { BaseOption Network []string `inbound:"network"` Target string `inbound:"target"` - Proxy string `inbound:"proxy,omitempty"` } func (o TunnelOption) Equal(config C.InboundConfig) bool { @@ -74,11 +73,11 @@ func (t *Tunnel) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter for _, network := range t.config.Network { switch network { case "tcp": - if t.ttl, err = tunnel.New(t.RawAddress(), t.config.Target, t.config.Proxy, tcpIn, t.Additions()...); err != nil { + if t.ttl, err = tunnel.New(t.RawAddress(), t.config.Target, t.config.SpecialProxy, tcpIn, t.Additions()...); err != nil { return err } case "udp": - if t.tul, err = tunnel.NewUDP(t.RawAddress(), t.config.Target, t.config.Proxy, udpIn, t.Additions()...); err != nil { + if t.tul, err = tunnel.NewUDP(t.RawAddress(), t.config.Target, t.config.SpecialProxy, udpIn, t.Additions()...); err != nil { return err } default: From dcbe25c3aea94a637a3a31fe69df67016300b598 Mon Sep 17 00:00:00 2001 From: Skyxim Date: Mon, 5 Dec 2022 19:48:54 +0800 Subject: [PATCH 205/250] chore: add log --- tunnel/tunnel.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index 64ed0325..8f0ef8f6 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -509,8 +509,10 @@ func match(metadata *C.Metadata) (C.Proxy, C.Rule, error) { func getRules(metadata *C.Metadata) []C.Rule { if sr, ok := subRules[metadata.SpecialRules]; ok { + log.Debugln("[Rule] use %s rules", metadata.SpecialRules) return sr } else { + log.Debugln("[Rule] use default rules") return rules } } From 2d2b75a4bf65ca14414fa130098bc2772db7da93 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 5 Dec 2022 21:32:08 +0800 Subject: [PATCH 206/250] doc: update tor listeners' tun --- docs/config.yaml | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/config.yaml b/docs/config.yaml index 0cb1ff78..4efa433e 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -759,13 +759,17 @@ listeners: # auto-detect-interface: false # 自动识别出口网卡 # auto-route: false # 配置路由表 # mtu: 9000 # 最大传输单元 + inet4-address: # 必须手动设置ipv4地址段 + - 198.19.0.1/30 + inet6-address: # 必须手动设置ipv6地址段 + - "fdfe:dcba:9877::1/126" # strict_route: true # 将所有连接路由到tun来防止泄漏,但你的设备将无法其他设备被访问 - inet4_route_address: # 启用 auto_route 时使用自定义路由而不是默认路由 - - 0.0.0.0/1 - - 128.0.0.0/1 - inet6_route_address: # 启用 auto_route 时使用自定义路由而不是默认路由 - - "::/1" - - "8000::/1" + # inet4_route_address: # 启用 auto_route 时使用自定义路由而不是默认路由 + # - 0.0.0.0/1 + # - 128.0.0.0/1 + # inet6_route_address: # 启用 auto_route 时使用自定义路由而不是默认路由 + # - "::/1" + # - "8000::/1" # endpoint_independent_nat: false # 启用独立于端点的 NAT # include_uid: # UID 规则仅在 Linux 下被支持,并且需要 auto_route # - 0 From 1c65a2c1b42668480d0ccf6fc5d85c561fe11056 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 5 Dec 2022 22:29:52 +0800 Subject: [PATCH 207/250] chore: cleanup rule parse code --- config/config.go | 82 ++++++++++++++---------------------------------- 1 file changed, 24 insertions(+), 58 deletions(-) diff --git a/config/config.go b/config/config.go index b5bc6a1d..b48d5328 100644 --- a/config/config.go +++ b/config/config.go @@ -489,14 +489,20 @@ func ParseRawConfig(rawCfg *RawConfig) (*Config, error) { } config.Listeners = listener - subRules, ruleProviders, err := parseSubRules(rawCfg, proxies) + log.Infoln("Geodata Loader mode: %s", geodata.LoaderName()) + ruleProviders, err := parseRuleProviders(rawCfg) + if err != nil { + return nil, err + } + config.RuleProviders = ruleProviders + + subRules, err := parseSubRules(rawCfg, proxies) if err != nil { return nil, err } config.SubRules = subRules - config.RuleProviders = ruleProviders - rules, err := parseRules(rawCfg, proxies, subRules) + rules, err := parseRules(rawCfg.Rule, proxies, subRules, "rules") if err != nil { return nil, err } @@ -712,75 +718,36 @@ func parseListeners(cfg *RawConfig) (listeners map[string]C.InboundListener, err return } -func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy) (subRules map[string][]C.Rule, ruleProviders map[string]providerTypes.RuleProvider, err error) { +func parseRuleProviders(cfg *RawConfig) (ruleProviders map[string]providerTypes.RuleProvider, err error) { ruleProviders = map[string]providerTypes.RuleProvider{} - subRules = map[string][]C.Rule{} - log.Infoln("Geodata Loader mode: %s", geodata.LoaderName()) // parse rule provider for name, mapping := range cfg.RuleProvider { rp, err := RP.ParseRuleProvider(name, mapping, R.ParseRule) if err != nil { - return nil, nil, err + return nil, err } ruleProviders[name] = rp RP.SetRuleProvider(rp) } + return +} +func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy) (subRules map[string][]C.Rule, err error) { for name, rawRules := range cfg.SubRules { if len(name) == 0 { - return nil, nil, fmt.Errorf("sub-rule name is empty") + return nil, fmt.Errorf("sub-rule name is empty") } var rules []C.Rule - for idx, line := range rawRules { - rawRule := trimArr(strings.Split(line, ",")) - var ( - payload string - target string - params []string - ruleName = strings.ToUpper(rawRule[0]) - ) - - l := len(rawRule) - - if ruleName == "NOT" || ruleName == "OR" || ruleName == "AND" || ruleName == "SUB-RULE" { - target = rawRule[l-1] - payload = strings.Join(rawRule[1:l-1], ",") - } else { - if l < 2 { - return nil, nil, fmt.Errorf("sub-rules[%d] [%s] error: format invalid", idx, line) - } - if l < 4 { - rawRule = append(rawRule, make([]string, 4-l)...) - } - if ruleName == "MATCH" { - l = 2 - } - if l >= 3 { - l = 3 - payload = rawRule[1] - } - target = rawRule[l-1] - params = rawRule[l:] - } - - if _, ok := proxies[target]; !ok && ruleName != "SUB-RULE" { - return nil, nil, fmt.Errorf("sub-rules[%d:%s] [%s] error: proxy [%s] not found", idx, name, line, target) - } - - params = trimArr(params) - parsed, parseErr := R.ParseRule(ruleName, payload, target, params, subRules) - if parseErr != nil { - return nil, nil, fmt.Errorf("sub-rules[%d] [%s] error: %s", idx, line, parseErr.Error()) - } - - rules = append(rules, parsed) + rules, err = parseRules(rawRules, proxies, subRules, fmt.Sprintf("sub-rules[%s]", name)) + if err != nil { + return nil, err } subRules[name] = rules } if err = verifySubRule(subRules); err != nil { - return nil, nil, err + return nil, err } return @@ -825,9 +792,8 @@ func verifySubRuleCircularReferences(n string, subRules map[string][]C.Rule, arr return nil } -func parseRules(cfg *RawConfig, proxies map[string]C.Proxy, subRules map[string][]C.Rule) ([]C.Rule, error) { +func parseRules(rulesConfig []string, proxies map[string]C.Proxy, subRules map[string][]C.Rule, format string) ([]C.Rule, error) { var rules []C.Rule - rulesConfig := cfg.Rule // parse rules for idx, line := range rulesConfig { @@ -846,7 +812,7 @@ func parseRules(cfg *RawConfig, proxies map[string]C.Proxy, subRules map[string] payload = strings.Join(rule[1:l-1], ",") } else { if l < 2 { - return nil, fmt.Errorf("rules[%d] [%s] error: format invalid", idx, line) + return nil, fmt.Errorf("%s[%d] [%s] error: format invalid", format, idx, line) } if l < 4 { rule = append(rule, make([]string, 4-l)...) @@ -863,16 +829,16 @@ func parseRules(cfg *RawConfig, proxies map[string]C.Proxy, subRules map[string] } if _, ok := proxies[target]; !ok { if ruleName != "SUB-RULE" { - return nil, fmt.Errorf("rules[%d] [%s] error: proxy [%s] not found", idx, line, target) + return nil, fmt.Errorf("%s[%d] [%s] error: proxy [%s] not found", format, idx, line, target) } else if _, ok = subRules[target]; !ok { - return nil, fmt.Errorf("rules[%d] [%s] error: sub-rule [%s] not found", idx, line, target) + return nil, fmt.Errorf("%s[%d] [%s] error: sub-rule [%s] not found", format, idx, line, target) } } params = trimArr(params) parsed, parseErr := R.ParseRule(ruleName, payload, target, params, subRules) if parseErr != nil { - return nil, fmt.Errorf("rules[%d] [%s] error: %s", idx, line, parseErr.Error()) + return nil, fmt.Errorf("%s[%d] [%s] error: %s", format, idx, line, parseErr.Error()) } rules = append(rules, parsed) From f390b9cf2fe290bfe230df4045616ee83534eeb2 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 5 Dec 2022 23:10:47 +0800 Subject: [PATCH 208/250] fix: inbound nil pointer --- listener/inbound/mixed.go | 2 +- listener/inbound/redir.go | 2 +- listener/inbound/tproxy.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/listener/inbound/mixed.go b/listener/inbound/mixed.go index 651ab486..a2920c69 100644 --- a/listener/inbound/mixed.go +++ b/listener/inbound/mixed.go @@ -57,7 +57,7 @@ func (m *Mixed) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) return err } if m.udp { - m.lUDP, err = socks.NewUDP(m.Address(), udpIn, m.Additions()...) + m.lUDP, err = socks.NewUDP(m.RawAddress(), udpIn, m.Additions()...) if err != nil { return err } diff --git a/listener/inbound/redir.go b/listener/inbound/redir.go index 37b5aab9..7a1685ba 100644 --- a/listener/inbound/redir.go +++ b/listener/inbound/redir.go @@ -44,7 +44,7 @@ func (r *Redir) Address() string { // Listen implements constant.InboundListener func (r *Redir) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) error { var err error - r.l, err = redir.New(r.Address(), tcpIn, r.Additions()...) + r.l, err = redir.New(r.RawAddress(), tcpIn, r.Additions()...) if err != nil { return err } diff --git a/listener/inbound/tproxy.go b/listener/inbound/tproxy.go index 223ebd93..7aa8af8d 100644 --- a/listener/inbound/tproxy.go +++ b/listener/inbound/tproxy.go @@ -57,7 +57,7 @@ func (t *TProxy) Listen(tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter } if t.udp { if t.lUDP != nil { - t.lUDP, err = tproxy.NewUDP(t.Address(), udpIn, t.Additions()...) + t.lUDP, err = tproxy.NewUDP(t.RawAddress(), udpIn, t.Additions()...) if err != nil { return err } From 3b96d54369b3d7f750bfb1491056292b10ab4795 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 5 Dec 2022 23:51:38 +0800 Subject: [PATCH 209/250] chore: cleanup rules/logic code --- rules/logic/and.go | 68 --------- rules/logic/common.go | 139 ------------------- rules/logic/logic.go | 289 +++++++++++++++++++++++++++++++++++++++ rules/logic/not.go | 63 --------- rules/logic/or.go | 67 --------- rules/logic/sub_rules.go | 91 ------------ 6 files changed, 289 insertions(+), 428 deletions(-) delete mode 100644 rules/logic/and.go delete mode 100644 rules/logic/common.go create mode 100644 rules/logic/logic.go delete mode 100644 rules/logic/not.go delete mode 100644 rules/logic/or.go delete mode 100644 rules/logic/sub_rules.go diff --git a/rules/logic/and.go b/rules/logic/and.go deleted file mode 100644 index ffd7c262..00000000 --- a/rules/logic/and.go +++ /dev/null @@ -1,68 +0,0 @@ -package logic - -import ( - "fmt" - C "github.com/Dreamacro/clash/constant" - "github.com/Dreamacro/clash/rules/common" - "strings" -) - -type AND struct { - *common.Base - rules []C.Rule - payload string - adapter string - needIP bool -} - -func (A *AND) ShouldFindProcess() bool { - return false -} - -func NewAND(payload string, adapter string, - parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) (*AND, error) { - and := &AND{Base: &common.Base{}, payload: payload, adapter: adapter} - rules, err := ParseRuleByPayload(payload, parse) - if err != nil { - return nil, err - } - - and.rules = rules - payloads := make([]string, 0, len(rules)) - for _, rule := range rules { - payloads = append(payloads, fmt.Sprintf("(%s,%s)", rule.RuleType().String(), rule.Payload())) - if rule.ShouldResolveIP() { - and.needIP = true - break - } - } - - and.payload = fmt.Sprintf("(%s)", strings.Join(payloads, " && ")) - return and, nil -} - -func (A *AND) RuleType() C.RuleType { - return C.AND -} - -func (A *AND) Match(metadata *C.Metadata) (bool, string) { - for _, rule := range A.rules { - if m, _ := rule.Match(metadata); !m { - return false, "" - } - } - - return true, A.adapter -} - -func (A *AND) Adapter() string { - return A.adapter -} - -func (A *AND) Payload() string { - return A.payload -} - -func (A *AND) ShouldResolveIP() bool { - return A.needIP -} diff --git a/rules/logic/common.go b/rules/logic/common.go deleted file mode 100644 index 1385463b..00000000 --- a/rules/logic/common.go +++ /dev/null @@ -1,139 +0,0 @@ -package logic - -import ( - "fmt" - "github.com/Dreamacro/clash/common/collections" - C "github.com/Dreamacro/clash/constant" - "regexp" - "strings" - _ "unsafe" -) - -func ParseRuleByPayload(payload string, parseRule func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) ([]C.Rule, error) { - regex, err := regexp.Compile("\\(.*\\)") - if err != nil { - return nil, err - } - - if regex.MatchString(payload) { - subAllRanges, err := format(payload) - if err != nil { - return nil, err - } - rules := make([]C.Rule, 0, len(subAllRanges)) - - subRanges := findSubRuleRange(payload, subAllRanges) - for _, subRange := range subRanges { - subPayload := payload[subRange.start+1 : subRange.end] - - rule, err := payloadToRule(subPayload, parseLogicSubRule(parseRule)) - if err != nil { - return nil, err - } - - rules = append(rules, rule) - } - - return rules, nil - } - - return nil, fmt.Errorf("payload format error") -} - -func containRange(r Range, preStart, preEnd int) bool { - return preStart < r.start && preEnd > r.end -} - -func payloadToRule(subPayload string, parseRule func(tp, payload, target string, params []string) (parsed C.Rule, parseErr error)) (C.Rule, error) { - splitStr := strings.SplitN(subPayload, ",", 2) - if len(splitStr) < 2 { - return nil, fmt.Errorf("[%s] format is error", subPayload) - } - - tp := splitStr[0] - payload := splitStr[1] - if tp == "NOT" || tp == "OR" || tp == "AND" { - return parseRule(tp, payload, "", nil) - } - param := strings.Split(payload, ",") - return parseRule(tp, param[0], "", param[1:]) -} - -func parseLogicSubRule(parseRule func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) func(tp, payload, target string, params []string) (parsed C.Rule, parseErr error) { - return func(tp, payload, target string, params []string) (parsed C.Rule, parseErr error) { - switch tp { - case "MATCH", "SUB-RULE": - return nil, fmt.Errorf("unsupported rule type [%s] on logic rule", tp) - default: - return parseRule(tp, payload, target, params, nil) - } - } -} - -type Range struct { - start int - end int - index int -} - -func format(payload string) ([]Range, error) { - stack := collections.NewStack() - num := 0 - subRanges := make([]Range, 0) - for i, c := range payload { - if c == '(' { - sr := Range{ - start: i, - index: num, - } - - num++ - stack.Push(sr) - } else if c == ')' { - if stack.Len() == 0 { - return nil, fmt.Errorf("missing '('") - } - - sr := stack.Pop().(Range) - sr.end = i - subRanges = append(subRanges, sr) - } - } - - if stack.Len() != 0 { - return nil, fmt.Errorf("format error is missing )") - } - - sortResult := make([]Range, len(subRanges)) - for _, sr := range subRanges { - sortResult[sr.index] = sr - } - - return sortResult, nil -} - -func findSubRuleRange(payload string, ruleRanges []Range) []Range { - payloadLen := len(payload) - subRuleRange := make([]Range, 0) - for _, rr := range ruleRanges { - if rr.start == 0 && rr.end == payloadLen-1 { - // 最大范围跳过 - continue - } - - containInSub := false - for _, r := range subRuleRange { - if containRange(rr, r.start, r.end) { - // The subRuleRange contains a range of rr, which is the next level node of the tree - containInSub = true - break - } - } - - if !containInSub { - subRuleRange = append(subRuleRange, rr) - } - } - - return subRuleRange -} diff --git a/rules/logic/logic.go b/rules/logic/logic.go new file mode 100644 index 00000000..4a0bd7e8 --- /dev/null +++ b/rules/logic/logic.go @@ -0,0 +1,289 @@ +package logic + +import ( + "fmt" + "regexp" + "strings" + + "github.com/Dreamacro/clash/common/collections" + C "github.com/Dreamacro/clash/constant" + "github.com/Dreamacro/clash/rules/common" +) + +type Logic struct { + *common.Base + payload string + adapter string + ruleType C.RuleType + rules []C.Rule + subRules map[string][]C.Rule + needIP bool + needProcess bool +} + +type ParseRuleFunc func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (C.Rule, error) + +func NewSubRule(payload, adapter string, subRules map[string][]C.Rule, parseRule ParseRuleFunc) (*Logic, error) { + logic := &Logic{Base: &common.Base{}, payload: payload, adapter: adapter, ruleType: C.SubRules} + err := logic.parsePayload(fmt.Sprintf("(%s)", payload), parseRule) + if err != nil { + return nil, err + } + + if len(logic.rules) != 1 { + return nil, fmt.Errorf("Sub-Rule rule must contain one rule") + } + for _, rule := range subRules[adapter] { + if rule.ShouldResolveIP() { + logic.needIP = true + } + if rule.ShouldFindProcess() { + logic.needProcess = true + } + } + logic.subRules = subRules + return logic, nil +} + +func NewNOT(payload string, adapter string, parseRule ParseRuleFunc) (*Logic, error) { + logic := &Logic{Base: &common.Base{}, payload: payload, adapter: adapter, ruleType: C.NOT} + err := logic.parsePayload(payload, parseRule) + if err != nil { + return nil, err + } + + if len(logic.rules) != 1 { + return nil, fmt.Errorf("not rule must contain one rule") + } + logic.needIP = logic.rules[0].ShouldResolveIP() + logic.needProcess = logic.rules[0].ShouldFindProcess() + logic.payload = fmt.Sprintf("(!(%s,%s))", logic.rules[0].RuleType(), logic.rules[0].Payload()) + return logic, nil +} + +func NewOR(payload string, adapter string, parseRule ParseRuleFunc) (*Logic, error) { + logic := &Logic{Base: &common.Base{}, payload: payload, adapter: adapter, ruleType: C.OR} + err := logic.parsePayload(payload, parseRule) + if err != nil { + return nil, err + } + + payloads := make([]string, 0, len(logic.rules)) + for _, rule := range logic.rules { + payloads = append(payloads, fmt.Sprintf("(%s,%s)", rule.RuleType().String(), rule.Payload())) + if rule.ShouldResolveIP() { + logic.needIP = true + } + if rule.ShouldFindProcess() { + logic.needProcess = true + } + } + logic.payload = fmt.Sprintf("(%s)", strings.Join(payloads, " || ")) + + return logic, nil +} +func NewAND(payload string, adapter string, parseRule ParseRuleFunc) (*Logic, error) { + logic := &Logic{Base: &common.Base{}, payload: payload, adapter: adapter, ruleType: C.AND} + err := logic.parsePayload(payload, parseRule) + if err != nil { + return nil, err + } + + payloads := make([]string, 0, len(logic.rules)) + for _, rule := range logic.rules { + payloads = append(payloads, fmt.Sprintf("(%s,%s)", rule.RuleType().String(), rule.Payload())) + if rule.ShouldResolveIP() { + logic.needIP = true + } + if rule.ShouldFindProcess() { + logic.needProcess = true + } + } + logic.payload = fmt.Sprintf("(%s)", strings.Join(payloads, " && ")) + + return logic, nil +} + +type Range struct { + start int + end int + index int +} + +func (r Range) containRange(preStart, preEnd int) bool { + return preStart < r.start && preEnd > r.end +} + +func (logic *Logic) payloadToRule(subPayload string, parseRule ParseRuleFunc) (C.Rule, error) { + splitStr := strings.SplitN(subPayload, ",", 2) + if len(splitStr) < 2 { + return nil, fmt.Errorf("[%s] format is error", subPayload) + } + + tp := splitStr[0] + payload := splitStr[1] + switch tp { + case "MATCH", "SUB-RULE": + return nil, fmt.Errorf("unsupported rule type [%s] on logic rule", tp) + case "NOT", "OR", "AND": + return parseRule(tp, payload, "", nil, nil) + } + param := strings.Split(payload, ",") + return parseRule(tp, param[0], "", param[1:], nil) +} + +func (logic *Logic) format(payload string) ([]Range, error) { + stack := collections.NewStack() + num := 0 + subRanges := make([]Range, 0) + for i, c := range payload { + if c == '(' { + sr := Range{ + start: i, + index: num, + } + + num++ + stack.Push(sr) + } else if c == ')' { + if stack.Len() == 0 { + return nil, fmt.Errorf("missing '('") + } + + sr := stack.Pop().(Range) + sr.end = i + subRanges = append(subRanges, sr) + } + } + + if stack.Len() != 0 { + return nil, fmt.Errorf("format error is missing )") + } + + sortResult := make([]Range, len(subRanges)) + for _, sr := range subRanges { + sortResult[sr.index] = sr + } + + return sortResult, nil +} + +func (logic *Logic) findSubRuleRange(payload string, ruleRanges []Range) []Range { + payloadLen := len(payload) + subRuleRange := make([]Range, 0) + for _, rr := range ruleRanges { + if rr.start == 0 && rr.end == payloadLen-1 { + // 最大范围跳过 + continue + } + + containInSub := false + for _, r := range subRuleRange { + if rr.containRange(r.start, r.end) { + // The subRuleRange contains a range of rr, which is the next level node of the tree + containInSub = true + break + } + } + + if !containInSub { + subRuleRange = append(subRuleRange, rr) + } + } + + return subRuleRange +} + +func (logic *Logic) parsePayload(payload string, parseRule ParseRuleFunc) error { + regex, err := regexp.Compile("\\(.*\\)") + if err != nil { + return err + } + + if regex.MatchString(payload) { + subAllRanges, err := logic.format(payload) + if err != nil { + return err + } + rules := make([]C.Rule, 0, len(subAllRanges)) + + subRanges := logic.findSubRuleRange(payload, subAllRanges) + for _, subRange := range subRanges { + subPayload := payload[subRange.start+1 : subRange.end] + + rule, err := logic.payloadToRule(subPayload, parseRule) + if err != nil { + return err + } + + rules = append(rules, rule) + } + + logic.rules = rules + + return nil + } + + return fmt.Errorf("payload format error") +} + +func (logic *Logic) RuleType() C.RuleType { + return logic.ruleType +} + +func matchSubRules(metadata *C.Metadata, name string, subRules map[string][]C.Rule) (bool, string) { + for _, rule := range subRules[name] { + if m, a := rule.Match(metadata); m { + if rule.RuleType() == C.SubRules { + matchSubRules(metadata, rule.Adapter(), subRules) + } else { + return m, a + } + } + } + return false, "" +} + +func (logic *Logic) Match(metadata *C.Metadata) (bool, string) { + switch logic.ruleType { + case C.SubRules: + return matchSubRules(metadata, logic.adapter, logic.subRules) + case C.NOT: + if m, _ := logic.rules[0].Match(metadata); !m { + return true, logic.adapter + } + return false, "" + case C.OR: + for _, rule := range logic.rules { + if m, _ := rule.Match(metadata); m { + return true, logic.adapter + } + } + return false, "" + case C.AND: + for _, rule := range logic.rules { + if m, _ := rule.Match(metadata); !m { + return false, logic.adapter + } + } + return true, logic.adapter + } + + return false, "" +} + +func (logic *Logic) Adapter() string { + return logic.adapter +} + +func (logic *Logic) Payload() string { + return logic.payload +} + +func (logic *Logic) ShouldResolveIP() bool { + return logic.needIP +} + +func (logic *Logic) ShouldFindProcess() bool { + return logic.needProcess +} diff --git a/rules/logic/not.go b/rules/logic/not.go deleted file mode 100644 index c109a2c9..00000000 --- a/rules/logic/not.go +++ /dev/null @@ -1,63 +0,0 @@ -package logic - -import ( - "fmt" - C "github.com/Dreamacro/clash/constant" - "github.com/Dreamacro/clash/rules/common" -) - -type NOT struct { - *common.Base - rule C.Rule - payload string - adapter string -} - -func (not *NOT) ShouldFindProcess() bool { - return false -} - -func NewNOT(payload string, adapter string, parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) (*NOT, error) { - not := &NOT{Base: &common.Base{}, adapter: adapter} - rule, err := ParseRuleByPayload(payload, parse) - if err != nil { - return nil, err - } - - if len(rule) != 1 { - return nil, fmt.Errorf("not rule must contain one rule") - } - - not.rule = rule[0] - not.payload = fmt.Sprintf("(!(%s,%s))", rule[0].RuleType(), rule[0].Payload()) - - return not, nil -} - -func (not *NOT) RuleType() C.RuleType { - return C.NOT -} - -func (not *NOT) Match(metadata *C.Metadata) (bool, string) { - if not.rule == nil { - return true, not.adapter - } - - if m, _ := not.rule.Match(metadata); !m { - return true, not.adapter - } - - return false, "" -} - -func (not *NOT) Adapter() string { - return not.adapter -} - -func (not *NOT) Payload() string { - return not.payload -} - -func (not *NOT) ShouldResolveIP() bool { - return not.rule != nil && not.rule.ShouldResolveIP() -} diff --git a/rules/logic/or.go b/rules/logic/or.go deleted file mode 100644 index f63bae4b..00000000 --- a/rules/logic/or.go +++ /dev/null @@ -1,67 +0,0 @@ -package logic - -import ( - "fmt" - C "github.com/Dreamacro/clash/constant" - "github.com/Dreamacro/clash/rules/common" - "strings" -) - -type OR struct { - *common.Base - rules []C.Rule - payload string - adapter string - needIP bool -} - -func (or *OR) ShouldFindProcess() bool { - return false -} - -func (or *OR) RuleType() C.RuleType { - return C.OR -} - -func (or *OR) Match(metadata *C.Metadata) (bool, string) { - for _, rule := range or.rules { - if m, _ := rule.Match(metadata); m { - return true, or.adapter - } - } - - return false, "" -} - -func (or *OR) Adapter() string { - return or.adapter -} - -func (or *OR) Payload() string { - return or.payload -} - -func (or *OR) ShouldResolveIP() bool { - return or.needIP -} - -func NewOR(payload string, adapter string, parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) (*OR, error) { - or := &OR{Base: &common.Base{}, payload: payload, adapter: adapter} - rules, err := ParseRuleByPayload(payload, parse) - if err != nil { - return nil, err - } - - or.rules = rules - payloads := make([]string, 0, len(rules)) - for _, rule := range rules { - payloads = append(payloads, fmt.Sprintf("(%s,%s)", rule.RuleType(), rule.Payload())) - if rule.ShouldResolveIP() { - or.needIP = true - break - } - } - - or.payload = fmt.Sprintf("(%s)", strings.Join(payloads, " || ")) - return or, nil -} diff --git a/rules/logic/sub_rules.go b/rules/logic/sub_rules.go deleted file mode 100644 index 75852979..00000000 --- a/rules/logic/sub_rules.go +++ /dev/null @@ -1,91 +0,0 @@ -package logic - -import ( - "fmt" - - C "github.com/Dreamacro/clash/constant" - "github.com/Dreamacro/clash/rules/common" -) - -type SubRule struct { - *common.Base - payload string - payloadRule C.Rule - subName string - subRules map[string][]C.Rule - shouldFindProcess *bool - shouldResolveIP *bool -} - -func NewSubRule(payload, subName string, sub map[string][]C.Rule, - parse func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error)) (*SubRule, error) { - payloadRule, err := ParseRuleByPayload(fmt.Sprintf("(%s)", payload), parse) - if err != nil { - return nil, err - } - if len(payloadRule) != 1 { - return nil, fmt.Errorf("Sub-Rule rule must contain one rule") - } - - return &SubRule{ - Base: &common.Base{}, - payload: payload, - payloadRule: payloadRule[0], - subName: subName, - subRules: sub, - }, nil -} - -func (r *SubRule) RuleType() C.RuleType { - return C.SubRules -} - -func (r *SubRule) Match(metadata *C.Metadata) (bool, string) { - - return match(metadata, r.subName, r.subRules) -} - -func match(metadata *C.Metadata, name string, subRules map[string][]C.Rule) (bool, string) { - for _, rule := range subRules[name] { - if m, a := rule.Match(metadata); m { - if rule.RuleType() == C.SubRules { - match(metadata, rule.Adapter(), subRules) - } else { - return m, a - } - } - } - return false, "" -} - -func (r *SubRule) ShouldResolveIP() bool { - if r.shouldResolveIP == nil { - s := false - for _, rule := range r.subRules[r.subName] { - s = s || rule.ShouldResolveIP() - } - r.shouldResolveIP = &s - } - - return *r.shouldResolveIP -} - -func (r *SubRule) ShouldFindProcess() bool { - if r.shouldFindProcess == nil { - s := false - for _, rule := range r.subRules[r.subName] { - s = s || rule.ShouldFindProcess() - } - r.shouldFindProcess = &s - } - - return *r.shouldFindProcess -} - -func (r *SubRule) Adapter() string { - return r.subName -} - -func (r *SubRule) Payload() string { - return r.payload -} From f7fb5840cfaf0e29e2967c6a9258115bdc146d56 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 6 Dec 2022 08:23:30 +0800 Subject: [PATCH 210/250] fix: reorder metadata --- constant/metadata.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/constant/metadata.go b/constant/metadata.go index 278304bd..359329f9 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -125,15 +125,15 @@ type Metadata struct { DstPort string `json:"destinationPort"` InIP netip.Addr `json:"inboundIP"` InPort string `json:"inboundPort"` + InName string `json:"inboundName"` Host string `json:"host"` DNSMode DNSMode `json:"dnsMode"` Uid *uint32 `json:"uid"` Process string `json:"process"` ProcessPath string `json:"processPath"` SpecialProxy string `json:"specialProxy"` - RemoteDst string `json:"remoteDestination"` - InName string `json:"inboundName"` SpecialRules string `json:"specialRules"` + RemoteDst string `json:"remoteDestination"` } func (m *Metadata) RemoteAddress() string { From b5b06ea49ccf9940e4758bb0924ba0bdf04ca941 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 6 Dec 2022 09:04:30 +0800 Subject: [PATCH 211/250] fix: config crash --- config/config.go | 92 +++++++------------------------------ hub/executor/executor.go | 4 +- hub/route/configs.go | 13 +++--- listener/config/tuic.go | 20 ++++---- listener/config/tun.go | 45 +++++++++--------- listener/inbound/tun.go | 12 +---- listener/listener.go | 2 +- listener/sing_tun/server.go | 11 ++++- 8 files changed, 69 insertions(+), 130 deletions(-) diff --git a/config/config.go b/config/config.go index b48d5328..6d761709 100644 --- a/config/config.go +++ b/config/config.go @@ -2,7 +2,6 @@ package config import ( "container/list" - "encoding/json" "errors" "fmt" "net" @@ -46,18 +45,18 @@ type General struct { Controller Mode T.TunnelMode `json:"mode"` UnifiedDelay bool - LogLevel log.LogLevel `json:"log-level"` - IPv6 bool `json:"ipv6"` - Interface string `json:"interface-name"` - RoutingMark int `json:"-"` - GeodataMode bool `json:"geodata-mode"` - GeodataLoader string `json:"geodata-loader"` - TCPConcurrent bool `json:"tcp-concurrent"` - EnableProcess bool `json:"enable-process"` - Tun Tun `json:"tun"` - TuicServer TuicServer `json:"tuic-server"` - Sniffing bool `json:"sniffing"` - EBpf EBpf `json:"-"` + LogLevel log.LogLevel `json:"log-level"` + IPv6 bool `json:"ipv6"` + Interface string `json:"interface-name"` + RoutingMark int `json:"-"` + GeodataMode bool `json:"geodata-mode"` + GeodataLoader string `json:"geodata-loader"` + TCPConcurrent bool `json:"tcp-concurrent"` + EnableProcess bool `json:"enable-process"` + Tun LC.Tun `json:"tun"` + TuicServer LC.TuicServer `json:"tuic-server"` + Sniffing bool `json:"sniffing"` + EBpf EBpf `json:"-"` } // Inbound config @@ -115,56 +114,11 @@ type Profile struct { StoreFakeIP bool `yaml:"store-fake-ip"` } -type TuicServer struct { - Enable bool `yaml:"enable" json:"enable"` - Listen string `yaml:"listen" json:"listen"` - Token []string `yaml:"token" json:"token"` - Certificate string `yaml:"certificate" json:"certificate"` - PrivateKey string `yaml:"private-key" json:"private-key"` - CongestionController string `yaml:"congestion-controller" json:"congestion-controller,omitempty"` - MaxIdleTime int `yaml:"max-idle-time" json:"max-idle-time,omitempty"` - AuthenticationTimeout int `yaml:"authentication-timeout" json:"authentication-timeout,omitempty"` - ALPN []string `yaml:"alpn" json:"alpn,omitempty"` - MaxUdpRelayPacketSize int `yaml:"max-udp-relay-packet-size" json:"max-udp-relay-packet-size,omitempty"` -} - type TLS struct { Certificate string `yaml:"certificate"` PrivateKey string `yaml:"private-key"` } -func (t TuicServer) String() string { - b, _ := json.Marshal(t) - return string(b) -} - -// Tun config -type Tun struct { - Enable bool `yaml:"enable" json:"enable"` - Device string `yaml:"device" json:"device"` - Stack C.TUNStack `yaml:"stack" json:"stack"` - DNSHijack []netip.AddrPort `yaml:"dns-hijack" json:"dns-hijack"` - AutoRoute bool `yaml:"auto-route" json:"auto-route"` - AutoDetectInterface bool `yaml:"auto-detect-interface" json:"auto-detect-interface"` - RedirectToTun []string `yaml:"-" json:"-"` - - MTU uint32 `yaml:"mtu" json:"mtu,omitempty"` - Inet4Address []LC.ListenPrefix `yaml:"inet4-address" json:"inet4-address,omitempty"` - Inet6Address []LC.ListenPrefix `yaml:"inet6-address" json:"inet6-address,omitempty"` - StrictRoute bool `yaml:"strict-route" json:"strict-route,omitempty"` - Inet4RouteAddress []LC.ListenPrefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"` - Inet6RouteAddress []LC.ListenPrefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"` - IncludeUID []uint32 `yaml:"include-uid" json:"include-uid,omitempty"` - IncludeUIDRange []string `yaml:"include-uid-range" json:"include-uid-range,omitempty"` - ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"` - ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"` - IncludeAndroidUser []int `yaml:"include-android-user" json:"include-android-user,omitempty"` - IncludePackage []string `yaml:"include-package" json:"include-package,omitempty"` - ExcludePackage []string `yaml:"exclude-package" json:"exclude-package,omitempty"` - EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"` - UDPTimeout int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"` -} - // IPTables config type IPTables struct { Enable bool `yaml:"enable" json:"enable"` @@ -734,6 +688,7 @@ func parseRuleProviders(cfg *RawConfig) (ruleProviders map[string]providerTypes. } func parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy) (subRules map[string][]C.Rule, err error) { + subRules = map[string][]C.Rule{} for name, rawRules := range cfg.SubRules { if len(name) == 0 { return nil, fmt.Errorf("sub-rule name is empty") @@ -1162,21 +1117,6 @@ func parseAuthentication(rawRecords []string) []auth.AuthUser { } func parseTun(rawTun RawTun, general *General) error { - var dnsHijack []netip.AddrPort - - for _, d := range rawTun.DNSHijack { - if _, after, ok := strings.Cut(d, "://"); ok { - d = after - } - d = strings.Replace(d, "any", "0.0.0.0", 1) - addrPort, err := netip.ParseAddrPort(d) - if err != nil { - return fmt.Errorf("parse dns-hijack url error: %w", err) - } - - dnsHijack = append(dnsHijack, addrPort) - } - tunAddressPrefix := T.FakeIPRange() if !tunAddressPrefix.IsValid() { tunAddressPrefix = netip.MustParsePrefix("198.18.0.1/16") @@ -1187,11 +1127,11 @@ func parseTun(rawTun RawTun, general *General) error { rawTun.Inet6Address = nil } - general.Tun = Tun{ + general.Tun = LC.Tun{ Enable: rawTun.Enable, Device: rawTun.Device, Stack: rawTun.Stack, - DNSHijack: dnsHijack, + DNSHijack: rawTun.DNSHijack, AutoRoute: rawTun.AutoRoute, AutoDetectInterface: rawTun.AutoDetectInterface, RedirectToTun: rawTun.RedirectToTun, @@ -1217,7 +1157,7 @@ func parseTun(rawTun RawTun, general *General) error { } func parseTuicServer(rawTuic RawTuicServer, general *General) error { - general.TuicServer = TuicServer{ + general.TuicServer = LC.TuicServer{ Enable: rawTuic.Enable, Listen: rawTuic.Listen, Token: rawTuic.Token, diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 32737c02..9c34ff82 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -125,8 +125,8 @@ func GetGeneral() *config.General { LogLevel: log.Level(), IPv6: !resolver.DisableIPv6, GeodataLoader: G.LoaderName(), - Tun: config.Tun(listener.GetTunConf()), - TuicServer: config.TuicServer(listener.GetTuicConf()), + Tun: listener.GetTunConf(), + TuicServer: listener.GetTuicConf(), Interface: dialer.DefaultInterface.Load(), Sniffing: tunnel.IsSniffing(), TCPConcurrent: dialer.GetDial(), diff --git a/hub/route/configs.go b/hub/route/configs.go index 3699b9d3..37acac5e 100644 --- a/hub/route/configs.go +++ b/hub/route/configs.go @@ -2,7 +2,6 @@ package route import ( "net/http" - "net/netip" "path/filepath" "sync" @@ -58,12 +57,12 @@ type configSchema struct { } type tunSchema struct { - Enable bool `yaml:"enable" json:"enable"` - Device *string `yaml:"device" json:"device"` - Stack *C.TUNStack `yaml:"stack" json:"stack"` - DNSHijack *[]netip.AddrPort `yaml:"dns-hijack" json:"dns-hijack"` - AutoRoute *bool `yaml:"auto-route" json:"auto-route"` - AutoDetectInterface *bool `yaml:"auto-detect-interface" json:"auto-detect-interface"` + Enable bool `yaml:"enable" json:"enable"` + Device *string `yaml:"device" json:"device"` + Stack *C.TUNStack `yaml:"stack" json:"stack"` + DNSHijack *[]string `yaml:"dns-hijack" json:"dns-hijack"` + AutoRoute *bool `yaml:"auto-route" json:"auto-route"` + AutoDetectInterface *bool `yaml:"auto-detect-interface" json:"auto-detect-interface"` //RedirectToTun []string `yaml:"-" json:"-"` MTU *uint32 `yaml:"mtu" json:"mtu,omitempty"` diff --git a/listener/config/tuic.go b/listener/config/tuic.go index 78252fd3..c584bbf5 100644 --- a/listener/config/tuic.go +++ b/listener/config/tuic.go @@ -5,16 +5,16 @@ import ( ) type TuicServer struct { - Enable bool - Listen string - Token []string - Certificate string - PrivateKey string - CongestionController string - MaxIdleTime int - AuthenticationTimeout int - ALPN []string - MaxUdpRelayPacketSize int + Enable bool `yaml:"enable" json:"enable"` + Listen string `yaml:"listen" json:"listen"` + Token []string `yaml:"token" json:"token"` + Certificate string `yaml:"certificate" json:"certificate"` + PrivateKey string `yaml:"private-key" json:"private-key"` + CongestionController string `yaml:"congestion-controller" json:"congestion-controller,omitempty"` + MaxIdleTime int `yaml:"max-idle-time" json:"max-idle-time,omitempty"` + AuthenticationTimeout int `yaml:"authentication-timeout" json:"authentication-timeout,omitempty"` + ALPN []string `yaml:"alpn" json:"alpn,omitempty"` + MaxUdpRelayPacketSize int `yaml:"max-udp-relay-packet-size" json:"max-udp-relay-packet-size,omitempty"` } func (t TuicServer) String() string { diff --git a/listener/config/tun.go b/listener/config/tun.go index e7b2c930..2e1d1a71 100644 --- a/listener/config/tun.go +++ b/listener/config/tun.go @@ -5,6 +5,7 @@ import ( "net/netip" C "github.com/Dreamacro/clash/constant" + "gopkg.in/yaml.v3" ) @@ -71,27 +72,27 @@ func StringSliceToListenPrefixSlice(ss []string) ([]ListenPrefix, error) { } type Tun struct { - Enable bool - Device string - Stack C.TUNStack - DNSHijack []netip.AddrPort - AutoRoute bool - AutoDetectInterface bool - RedirectToTun []string + Enable bool `yaml:"enable" json:"enable"` + Device string `yaml:"device" json:"device"` + Stack C.TUNStack `yaml:"stack" json:"stack"` + DNSHijack []string `yaml:"dns-hijack" json:"dns-hijack"` + AutoRoute bool `yaml:"auto-route" json:"auto-route"` + AutoDetectInterface bool `yaml:"auto-detect-interface" json:"auto-detect-interface"` + RedirectToTun []string `yaml:"-" json:"-"` - MTU uint32 - Inet4Address []ListenPrefix - Inet6Address []ListenPrefix - StrictRoute bool - Inet4RouteAddress []ListenPrefix - Inet6RouteAddress []ListenPrefix - IncludeUID []uint32 - IncludeUIDRange []string - ExcludeUID []uint32 - ExcludeUIDRange []string - IncludeAndroidUser []int - IncludePackage []string - ExcludePackage []string - EndpointIndependentNat bool - UDPTimeout int64 + MTU uint32 `yaml:"mtu" json:"mtu,omitempty"` + Inet4Address []ListenPrefix `yaml:"inet4-address" json:"inet4-address,omitempty"` + Inet6Address []ListenPrefix `yaml:"inet6-address" json:"inet6-address,omitempty"` + StrictRoute bool `yaml:"strict-route" json:"strict-route,omitempty"` + Inet4RouteAddress []ListenPrefix `yaml:"inet4-route-address" json:"inet4-route-address,omitempty"` + Inet6RouteAddress []ListenPrefix `yaml:"inet6-route-address" json:"inet6-route-address,omitempty"` + IncludeUID []uint32 `yaml:"include-uid" json:"include-uid,omitempty"` + IncludeUIDRange []string `yaml:"include-uid-range" json:"include-uid-range,omitempty"` + ExcludeUID []uint32 `yaml:"exclude-uid" json:"exclude-uid,omitempty"` + ExcludeUIDRange []string `yaml:"exclude-uid-range" json:"exclude-uid-range,omitempty"` + IncludeAndroidUser []int `yaml:"include-android-user" json:"include-android-user,omitempty"` + IncludePackage []string `yaml:"include-package" json:"include-package,omitempty"` + ExcludePackage []string `yaml:"exclude-package" json:"exclude-package,omitempty"` + EndpointIndependentNat bool `yaml:"endpoint-independent-nat" json:"endpoint-independent-nat,omitempty"` + UDPTimeout int64 `yaml:"udp-timeout" json:"udp-timeout,omitempty"` } diff --git a/listener/inbound/tun.go b/listener/inbound/tun.go index 3365ed89..997164c2 100644 --- a/listener/inbound/tun.go +++ b/listener/inbound/tun.go @@ -2,7 +2,6 @@ package inbound import ( "errors" - "net/netip" "strings" C "github.com/Dreamacro/clash/constant" @@ -56,15 +55,6 @@ func NewTun(options *TunOption) (*Tun, error) { if !exist { return nil, errors.New("invalid tun stack") } - dnsHijack := make([]netip.AddrPort, 0, len(options.DNSHijack)) - for _, str := range options.DNSHijack { - var a netip.AddrPort - err = a.UnmarshalText([]byte(str)) - if err != nil { - return nil, err - } - dnsHijack = append(dnsHijack, a) - } inet4Address, err := LC.StringSliceToListenPrefixSlice(options.Inet4Address) if err != nil { return nil, err @@ -88,7 +78,7 @@ func NewTun(options *TunOption) (*Tun, error) { Enable: true, Device: options.Device, Stack: stack, - DNSHijack: dnsHijack, + DNSHijack: options.DNSHijack, AutoRoute: options.AutoRoute, AutoDetectInterface: options.AutoDetectInterface, MTU: options.MTU, diff --git a/listener/listener.go b/listener/listener.go index ab9963b6..26eb117b 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -825,7 +825,7 @@ func hasTunConfigChange(tunConf *LC.Tun) bool { } sort.Slice(tunConf.DNSHijack, func(i, j int) bool { - return tunConf.DNSHijack[i].Addr().Less(tunConf.DNSHijack[j].Addr()) + return tunConf.DNSHijack[i] < tunConf.DNSHijack[j] }) sort.Slice(tunConf.Inet4Address, func(i, j int) bool { diff --git a/listener/sing_tun/server.go b/listener/sing_tun/server.go index d74324da..5c387a8d 100644 --- a/listener/sing_tun/server.go +++ b/listener/sing_tun/server.go @@ -109,7 +109,16 @@ func New(options LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapte var dnsAdds []netip.AddrPort for _, d := range options.DNSHijack { - dnsAdds = append(dnsAdds, d) + if _, after, ok := strings.Cut(d, "://"); ok { + d = after + } + d = strings.Replace(d, "any", "0.0.0.0", 1) + addrPort, err := netip.ParseAddrPort(d) + if err != nil { + return nil, fmt.Errorf("parse dns-hijack url error: %w", err) + } + + dnsAdds = append(dnsAdds, addrPort) } for _, a := range options.Inet4Address { addrPort := netip.AddrPortFrom(a.Build().Addr().Next(), 53) From cd99b2e79510b15cc66b7d68d6041640189f2430 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 6 Dec 2022 10:13:05 +0800 Subject: [PATCH 212/250] fix: config code merge --- config/config.go | 101 +++++++++++++------------- hub/executor/executor.go | 7 +- listener/{tunnel => config}/tunnel.go | 2 +- listener/listener.go | 4 +- 4 files changed, 56 insertions(+), 58 deletions(-) rename listener/{tunnel => config}/tunnel.go (98%) diff --git a/config/config.go b/config/config.go index 6d761709..6695fd11 100644 --- a/config/config.go +++ b/config/config.go @@ -30,7 +30,6 @@ import ( "github.com/Dreamacro/clash/dns" L "github.com/Dreamacro/clash/listener" LC "github.com/Dreamacro/clash/listener/config" - "github.com/Dreamacro/clash/listener/tunnel" "github.com/Dreamacro/clash/log" R "github.com/Dreamacro/clash/rules" RP "github.com/Dreamacro/clash/rules/provider" @@ -45,33 +44,33 @@ type General struct { Controller Mode T.TunnelMode `json:"mode"` UnifiedDelay bool - LogLevel log.LogLevel `json:"log-level"` - IPv6 bool `json:"ipv6"` - Interface string `json:"interface-name"` - RoutingMark int `json:"-"` - GeodataMode bool `json:"geodata-mode"` - GeodataLoader string `json:"geodata-loader"` - TCPConcurrent bool `json:"tcp-concurrent"` - EnableProcess bool `json:"enable-process"` - Tun LC.Tun `json:"tun"` - TuicServer LC.TuicServer `json:"tuic-server"` - Sniffing bool `json:"sniffing"` - EBpf EBpf `json:"-"` + LogLevel log.LogLevel `json:"log-level"` + IPv6 bool `json:"ipv6"` + Interface string `json:"interface-name"` + RoutingMark int `json:"-"` + GeodataMode bool `json:"geodata-mode"` + GeodataLoader string `json:"geodata-loader"` + TCPConcurrent bool `json:"tcp-concurrent"` + EnableProcess bool `json:"enable-process"` + Sniffing bool `json:"sniffing"` + EBpf EBpf `json:"-"` } // Inbound config type Inbound struct { - Port int `json:"port"` - SocksPort int `json:"socks-port"` - RedirPort int `json:"redir-port"` - TProxyPort int `json:"tproxy-port"` - MixedPort int `json:"mixed-port"` - ShadowSocksConfig string `json:"ss-config"` - VmessConfig string `json:"vmess-config"` - Authentication []string `json:"authentication"` - AllowLan bool `json:"allow-lan"` - BindAddress string `json:"bind-address"` - InboundTfo bool `json:"inbound-tfo"` + Port int `json:"port"` + SocksPort int `json:"socks-port"` + RedirPort int `json:"redir-port"` + TProxyPort int `json:"tproxy-port"` + MixedPort int `json:"mixed-port"` + Tun LC.Tun `json:"tun"` + TuicServer LC.TuicServer `json:"tuic-server"` + ShadowSocksConfig string `json:"ss-config"` + VmessConfig string `json:"vmess-config"` + Authentication []string `json:"authentication"` + AllowLan bool `json:"allow-lan"` + BindAddress string `json:"bind-address"` + InboundTfo bool `json:"inbound-tfo"` } // Controller config @@ -157,7 +156,7 @@ type Config struct { Listeners map[string]C.InboundListener Providers map[string]providerTypes.ProxyProvider RuleProviders map[string]providerTypes.RuleProvider - Tunnels []tunnel.Tunnel + Tunnels []LC.Tunnel Sniffer *Sniffer TLS *TLS } @@ -227,32 +226,32 @@ 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"` - ShadowSocksConfig string `yaml:"ss-config"` - VmessConfig string `yaml:"vmess-config"` - InboundTfo bool `yaml:"inbound-tfo"` - Authentication []string `yaml:"authentication"` - 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"` - ExternalController string `yaml:"external-controller"` - ExternalControllerTLS string `yaml:"external-controller-tls"` - ExternalUI string `yaml:"external-ui"` - Secret string `yaml:"secret"` - Interface string `yaml:"interface-name"` - RoutingMark int `yaml:"routing-mark"` - Tunnels []tunnel.Tunnel `yaml:"tunnels"` - GeodataMode bool `yaml:"geodata-mode"` - GeodataLoader string `yaml:"geodata-loader"` - TCPConcurrent bool `yaml:"tcp-concurrent" json:"tcp-concurrent"` - EnableProcess bool `yaml:"enable-process" json:"enable-process"` + Port int `yaml:"port"` + SocksPort int `yaml:"socks-port"` + RedirPort int `yaml:"redir-port"` + TProxyPort int `yaml:"tproxy-port"` + MixedPort int `yaml:"mixed-port"` + ShadowSocksConfig string `yaml:"ss-config"` + VmessConfig string `yaml:"vmess-config"` + InboundTfo bool `yaml:"inbound-tfo"` + Authentication []string `yaml:"authentication"` + 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"` + ExternalController string `yaml:"external-controller"` + ExternalControllerTLS string `yaml:"external-controller-tls"` + ExternalUI string `yaml:"external-ui"` + Secret string `yaml:"secret"` + 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"` + TCPConcurrent bool `yaml:"tcp-concurrent" json:"tcp-concurrent"` + EnableProcess bool `yaml:"enable-process" json:"enable-process"` Sniffer RawSniffer `yaml:"sniffer"` ProxyProvider map[string]map[string]any `yaml:"proxy-providers"` diff --git a/hub/executor/executor.go b/hub/executor/executor.go index 9c34ff82..eb4436fb 100644 --- a/hub/executor/executor.go +++ b/hub/executor/executor.go @@ -29,7 +29,6 @@ import ( LC "github.com/Dreamacro/clash/listener/config" "github.com/Dreamacro/clash/listener/inner" "github.com/Dreamacro/clash/listener/tproxy" - T "github.com/Dreamacro/clash/listener/tunnel" "github.com/Dreamacro/clash/log" "github.com/Dreamacro/clash/tunnel" ) @@ -115,6 +114,8 @@ func GetGeneral() *config.General { RedirPort: ports.RedirPort, TProxyPort: ports.TProxyPort, MixedPort: ports.MixedPort, + Tun: listener.GetTunConf(), + TuicServer: listener.GetTuicConf(), ShadowSocksConfig: ports.ShadowSocksConfig, VmessConfig: ports.VmessConfig, Authentication: authenticator, @@ -125,8 +126,6 @@ func GetGeneral() *config.General { LogLevel: log.Level(), IPv6: !resolver.DisableIPv6, GeodataLoader: G.LoaderName(), - Tun: listener.GetTunConf(), - TuicServer: listener.GetTuicConf(), Interface: dialer.DefaultInterface.Load(), Sniffing: tunnel.IsSniffing(), TCPConcurrent: dialer.GetDial(), @@ -304,7 +303,7 @@ func updateSniffer(sniffer *config.Sniffer) { } } -func updateTunnels(tunnels []T.Tunnel) { +func updateTunnels(tunnels []LC.Tunnel) { listener.PatchTunnel(tunnels, tunnel.TCPIn(), tunnel.UDPIn()) } diff --git a/listener/tunnel/tunnel.go b/listener/config/tunnel.go similarity index 98% rename from listener/tunnel/tunnel.go rename to listener/config/tunnel.go index c3fc759c..eadee4b1 100644 --- a/listener/tunnel/tunnel.go +++ b/listener/config/tunnel.go @@ -1,4 +1,4 @@ -package tunnel +package config import ( "fmt" diff --git a/listener/listener.go b/listener/listener.go index 26eb117b..5d7b98c2 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -625,7 +625,7 @@ func ReCreateAutoRedir(ifaceNames []string, tcpIn chan<- C.ConnContext, _ chan<- log.Infoln("Auto redirect proxy listening at: %s, attached tc ebpf program to interfaces %v", autoRedirListener.Address(), autoRedirProgram.RawNICs()) } -func PatchTunnel(tunnels []tunnel.Tunnel, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) { +func PatchTunnel(tunnels []LC.Tunnel, tcpIn chan<- C.ConnContext, udpIn chan<- C.PacketAdapter) { tunnelMux.Lock() defer tunnelMux.Unlock() @@ -664,7 +664,7 @@ func PatchTunnel(tunnels []tunnel.Tunnel, tcpIn chan<- C.ConnContext, udpIn chan newElm := lo.FlatMap( tunnels, - func(tunnel tunnel.Tunnel, _ int) []addrProxy { + func(tunnel LC.Tunnel, _ int) []addrProxy { return lo.Map( tunnel.Network, func(network string, _ int) addrProxy { From e03fcd24dd5a5bd769f193cb68a2c1b67c256b73 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 7 Dec 2022 13:00:45 +0800 Subject: [PATCH 213/250] fix: let any type of dns server can use remote proxy --- config/config.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/config/config.go b/config/config.go index 6695fd11..0a2cb963 100644 --- a/config/config.go +++ b/config/config.go @@ -855,7 +855,9 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error) return nil, fmt.Errorf("DNS NameServer[%d] format error: %s", idx, err.Error()) } - var addr, dnsNetType, proxyAdapter string + proxyAdapter := u.Fragment + + var addr, dnsNetType string params := map[string]string{} switch u.Scheme { case "udp": From a6f7e1472bf616b49b217c377061d2d32a3e9232 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Wed, 7 Dec 2022 20:01:44 +0800 Subject: [PATCH 214/250] fix: let doh/dot server follow hosts and can remotely resolve itself ip --- adapter/outbound/direct.go | 5 +-- component/dialer/dialer.go | 40 +++++++++++----------- component/dialer/options.go | 8 +++-- dns/client.go | 8 +---- dns/doh.go | 2 +- dns/doq.go | 28 +--------------- dns/util.go | 66 ++++++++++++++++++++++++------------- 7 files changed, 76 insertions(+), 81 deletions(-) diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go index fdbbcb62..d7ffd478 100644 --- a/adapter/outbound/direct.go +++ b/adapter/outbound/direct.go @@ -5,6 +5,7 @@ import ( "net" "github.com/Dreamacro/clash/component/dialer" + "github.com/Dreamacro/clash/component/resolver" C "github.com/Dreamacro/clash/constant" ) @@ -14,7 +15,7 @@ type Direct struct { // DialContext implements C.ProxyAdapter func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { - opts = append(opts, dialer.WithDirect()) + opts = append(opts, dialer.WithResolver(resolver.DefaultResolver)) c, err := dialer.DialContext(ctx, "tcp", metadata.RemoteAddress(), d.Base.DialOptions(opts...)...) if err != nil { return nil, err @@ -25,7 +26,7 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ... // ListenPacketContext implements C.ProxyAdapter func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { - opts = append(opts, dialer.WithDirect()) + opts = append(opts, dialer.WithResolver(resolver.DefaultResolver)) pc, err := dialer.ListenPacket(ctx, "udp", "", d.Base.DialOptions(opts...)...) if err != nil { return nil, err diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index cb87061c..44fa2e90 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -4,12 +4,14 @@ import ( "context" "errors" "fmt" - "github.com/Dreamacro/clash/component/resolver" - "go.uber.org/atomic" "net" "net/netip" "strings" "sync" + + "github.com/Dreamacro/clash/component/resolver" + + "go.uber.org/atomic" ) var ( @@ -149,7 +151,7 @@ func dualStackDialContext(ctx context.Context, network, address string, opt *opt results := make(chan dialResult) var primary, fallback dialResult - startRacer := func(ctx context.Context, network, host string, direct bool, ipv6 bool) { + startRacer := func(ctx context.Context, network, host string, r resolver.Resolver, ipv6 bool) { result := dialResult{ipv6: ipv6, done: true} defer func() { select { @@ -163,16 +165,16 @@ func dualStackDialContext(ctx context.Context, network, address string, opt *opt var ip netip.Addr if ipv6 { - if !direct { + if r == nil { ip, result.error = resolver.ResolveIPv6ProxyServerHost(ctx, host) } else { - ip, result.error = resolver.ResolveIPv6(ctx, host) + ip, result.error = resolver.ResolveIPv6WithResolver(ctx, host, r) } } else { - if !direct { + if r == nil { ip, result.error = resolver.ResolveIPv4ProxyServerHost(ctx, host) } else { - ip, result.error = resolver.ResolveIPv4(ctx, host) + ip, result.error = resolver.ResolveIPv4WithResolver(ctx, host, r) } } if result.error != nil { @@ -183,8 +185,8 @@ func dualStackDialContext(ctx context.Context, network, address string, opt *opt result.Conn, result.error = dialContext(ctx, network, ip, port, opt) } - go startRacer(ctx, network+"4", host, opt.direct, false) - go startRacer(ctx, network+"6", host, opt.direct, true) + go startRacer(ctx, network+"4", host, opt.resolver, false) + go startRacer(ctx, network+"6", host, opt.resolver, true) count := 2 for i := 0; i < count; i++ { @@ -230,8 +232,8 @@ func concurrentDualStackDialContext(ctx context.Context, network, address string } var ips []netip.Addr - if opt.direct { - ips, err = resolver.LookupIP(ctx, host) + if opt.resolver != nil { + ips, err = resolver.LookupIPWithResolver(ctx, host, opt.resolver) } else { ips, err = resolver.LookupIPProxyServerHost(ctx, host) } @@ -363,16 +365,16 @@ func singleDialContext(ctx context.Context, network string, address string, opt var ip netip.Addr switch network { case "tcp4", "udp4": - if !opt.direct { + if opt.resolver == nil { ip, err = resolver.ResolveIPv4ProxyServerHost(ctx, host) } else { - ip, err = resolver.ResolveIPv4(ctx, host) + ip, err = resolver.ResolveIPv4WithResolver(ctx, host, opt.resolver) } default: - if !opt.direct { + if opt.resolver == nil { ip, err = resolver.ResolveIPv6ProxyServerHost(ctx, host) } else { - ip, err = resolver.ResolveIPv6(ctx, host) + ip, err = resolver.ResolveIPv6WithResolver(ctx, host, opt.resolver) } } if err != nil { @@ -398,10 +400,10 @@ func concurrentIPv4DialContext(ctx context.Context, network, address string, opt } var ips []netip.Addr - if !opt.direct { + if opt.resolver == nil { ips, err = resolver.LookupIPv4ProxyServerHost(ctx, host) } else { - ips, err = resolver.LookupIPv4(ctx, host) + ips, err = resolver.LookupIPv4WithResolver(ctx, host, opt.resolver) } if err != nil { @@ -418,10 +420,10 @@ func concurrentIPv6DialContext(ctx context.Context, network, address string, opt } var ips []netip.Addr - if !opt.direct { + if opt.resolver == nil { ips, err = resolver.LookupIPv6ProxyServerHost(ctx, host) } else { - ips, err = resolver.LookupIPv6(ctx, host) + ips, err = resolver.LookupIPv6WithResolver(ctx, host, opt.resolver) } if err != nil { diff --git a/component/dialer/options.go b/component/dialer/options.go index ce911035..98d0b8bd 100644 --- a/component/dialer/options.go +++ b/component/dialer/options.go @@ -1,6 +1,8 @@ package dialer import ( + "github.com/Dreamacro/clash/component/resolver" + "go.uber.org/atomic" ) @@ -14,9 +16,9 @@ type option struct { interfaceName string addrReuse bool routingMark int - direct bool network int prefer int + resolver resolver.Resolver } type Option func(opt *option) @@ -39,9 +41,9 @@ func WithRoutingMark(mark int) Option { } } -func WithDirect() Option { +func WithResolver(r resolver.Resolver) Option { return func(opt *option) { - opt.direct = true + opt.resolver = r } } diff --git a/dns/client.go b/dns/client.go index 0a13469c..a7bf5eb3 100644 --- a/dns/client.go +++ b/dns/client.go @@ -60,13 +60,7 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) options = append(options, dialer.WithInterface(c.iface.Load())) } - var conn net.Conn - if c.proxyAdapter != "" { - conn, err = dialContextExtra(ctx, c.proxyAdapter, network, ip, c.port, options...) - } else { - conn, err = dialer.DialContext(ctx, network, net.JoinHostPort(ip.String(), c.port), options...) - } - + conn, err := getDialHandler(c.r, c.proxyAdapter, options...)(ctx, network, net.JoinHostPort(ip.String(), c.port)) if err != nil { return nil, err } diff --git a/dns/doh.go b/dns/doh.go index fc32a212..ac5d7486 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -536,7 +536,7 @@ func (doh *dnsOverHTTPS) dialQuic(ctx context.Context, addr string, tlsCfg *tls. return nil, err } } else { - if wrapConn, err := dialContextExtra(ctx, doh.proxyAdapter, "udp", udpAddr.AddrPort().Addr(), port); err == nil { + if wrapConn, err := dialContextExtra(ctx, doh.proxyAdapter, "udp", addr, doh.r); err == nil { if pc, ok := wrapConn.(*wrapPacketConn); ok { conn = pc } else { diff --git a/dns/doq.go b/dns/doq.go index d4fbb037..16a32a3a 100644 --- a/dns/doq.go +++ b/dns/doq.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "net" - "net/netip" "runtime" "strconv" "sync" @@ -41,8 +40,6 @@ const ( DefaultTimeout = time.Second * 5 ) -type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error) - // dnsOverQUIC is a struct that implements the Upstream interface for the // DNS-over-QUIC protocol (spec: https://www.rfc-editor.org/rfc/rfc9250.html). type dnsOverQUIC struct { @@ -345,12 +342,7 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connectio return nil, err } } else { - ipAddr, err := netip.ParseAddr(ip) - if err != nil { - return nil, err - } - - conn, err := dialContextExtra(ctx, doq.proxyAdapter, "udp", ipAddr, port) + conn, err := dialContextExtra(ctx, doq.proxyAdapter, "udp", addr, doq.r) if err != nil { return nil, err } @@ -498,21 +490,3 @@ func isQUICRetryError(err error) (ok bool) { return false } - -func getDialHandler(r *Resolver, proxyAdapter string) dialHandler { - return func(ctx context.Context, network, addr string) (net.Conn, error) { - host, port, err := net.SplitHostPort(addr) - if err != nil { - return nil, err - } - ip, err := r.ResolveIP(ctx, host) - if err != nil { - return nil, err - } - if len(proxyAdapter) == 0 { - return dialer.DialContext(ctx, network, net.JoinHostPort(ip.String(), port), dialer.WithDirect()) - } else { - return dialContextExtra(ctx, proxyAdapter, network, ip.Unmap(), port) - } - } -} diff --git a/dns/util.go b/dns/util.go index 5bf09b8f..fb42a9e7 100644 --- a/dns/util.go +++ b/dns/util.go @@ -160,27 +160,54 @@ func (wpc *wrapPacketConn) LocalAddr() net.Addr { } } -func dialContextExtra(ctx context.Context, adapterName string, network string, dstIP netip.Addr, port string, opts ...dialer.Option) (net.Conn, error) { - networkType := C.TCP - if network == "udp" { +type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error) - networkType = C.UDP +func getDialHandler(r *Resolver, proxyAdapter string, opts ...dialer.Option) dialHandler { + return func(ctx context.Context, network, addr string) (net.Conn, error) { + if len(proxyAdapter) == 0 { + opts = append(opts, dialer.WithResolver(r)) + return dialer.DialContext(ctx, network, addr, opts...) + } else { + return dialContextExtra(ctx, proxyAdapter, network, addr, r, opts...) + } } +} - metadata := &C.Metadata{ - NetWork: networkType, - Host: "", - DstIP: dstIP, - DstPort: port, +func dialContextExtra(ctx context.Context, adapterName string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.Conn, error) { + host, port, err := net.SplitHostPort(addr) + if err != nil { + return nil, err } - adapter, ok := tunnel.Proxies()[adapterName] if !ok { opts = append(opts, dialer.WithInterface(adapterName)) - if C.TCP == networkType { - return dialer.DialContext(ctx, network, dstIP.String()+":"+port, opts...) - } else { - packetConn, err := dialer.ListenPacket(ctx, network, dstIP.String()+":"+port, opts...) + } + if strings.Contains(network, "tcp") { + // tcp can resolve host by remote + metadata := &C.Metadata{ + NetWork: C.TCP, + Host: host, + DstPort: port, + } + if ok { + return adapter.DialContext(ctx, metadata, opts...) + } + opts = append(opts, dialer.WithResolver(r)) + return dialer.DialContext(ctx, network, addr, opts...) + } else { + // udp must resolve host first + dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r) + if err != nil { + return nil, err + } + metadata := &C.Metadata{ + NetWork: C.UDP, + Host: "", + DstIP: dstIP, + DstPort: port, + } + if !ok { + packetConn, err := dialer.ListenPacket(ctx, network, metadata.RemoteAddress(), opts...) if err != nil { return nil, err } @@ -189,15 +216,12 @@ func dialContextExtra(ctx context.Context, adapterName string, network string, d PacketConn: packetConn, rAddr: metadata.UDPAddr(), }, nil - } - } - if networkType == C.UDP && !adapter.SupportUDP() { - return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", adapterName) - } + if !adapter.SupportUDP() { + return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", adapterName) + } - if networkType == C.UDP { packetConn, err := adapter.ListenPacketContext(ctx, metadata, opts...) if err != nil { return nil, err @@ -208,8 +232,6 @@ func dialContextExtra(ctx context.Context, adapterName string, network string, d rAddr: metadata.UDPAddr(), }, nil } - - return adapter.DialContext(ctx, metadata, opts...) } func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) { From 78c7b6259c295ed6aa54d87bfc62267118ac461b Mon Sep 17 00:00:00 2001 From: Skyxim Date: Thu, 8 Dec 2022 09:59:04 +0800 Subject: [PATCH 215/250] chore: update config.yaml --- docs/config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/config.yaml b/docs/config.yaml index 4efa433e..b6fc74f9 100644 --- a/docs/config.yaml +++ b/docs/config.yaml @@ -136,7 +136,7 @@ profile: # DNS配置 dns: enable: false # 关闭将使用系统 DNS - perfer-h3: true # 开启 DoH 支持 HTTP/3,将并发尝试 + prefer-h3: true # 开启 DoH 支持 HTTP/3,将并发尝试 listen: 0.0.0.0:53 # 开启 DNS 服务器监听 # ipv6: false # false 将返回 AAAA 的空结果 From a9839abd4cbfad497274db4e41566f9d1cb45092 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 8 Dec 2022 11:29:39 +0800 Subject: [PATCH 216/250] fix: tun create panic --- listener/listener.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/listener/listener.go b/listener/listener.go index 5d7b98c2..d747d5f5 100644 --- a/listener/listener.go +++ b/listener/listener.go @@ -532,7 +532,12 @@ func ReCreateTun(tunConf LC.Tun, tcpIn chan<- C.ConnContext, udpIn chan<- C.Pack return } - tunLister, err = sing_tun.New(tunConf, tcpIn, udpIn) + lister, err := sing_tun.New(tunConf, tcpIn, udpIn) + if err != nil { + return + } + tunLister = lister + log.Infoln("[TUN] Tun adapter listening at: %s", tunLister.Address()) } From 910e7fed977b2c1a73fef1bd1c022ef13295753a Mon Sep 17 00:00:00 2001 From: Skyxim Date: Thu, 8 Dec 2022 12:46:46 +0800 Subject: [PATCH 217/250] fix: parse DoH url --- config/config.go | 1 + 1 file changed, 1 insertion(+) diff --git a/config/config.go b/config/config.go index 0a2cb963..da05877f 100644 --- a/config/config.go +++ b/config/config.go @@ -871,6 +871,7 @@ func parseNameServer(servers []string, preferH3 bool) ([]dns.NameServer, error) dnsNetType = "tcp-tls" // DNS over TLS case "https": host := u.Host + proxyAdapter = "" if _, _, err := net.SplitHostPort(host); err != nil && strings.Contains(err.Error(), "missing port in address") { host = net.JoinHostPort(host, "443") } else { From b5928c36a32fe914686d459a706689d976240148 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 11 Dec 2022 08:59:57 +0800 Subject: [PATCH 218/250] fix: tunnel panic --- listener/inbound/tunnel.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/listener/inbound/tunnel.go b/listener/inbound/tunnel.go index e3e16281..221f4cd6 100644 --- a/listener/inbound/tunnel.go +++ b/listener/inbound/tunnel.go @@ -64,7 +64,13 @@ func (t *Tunnel) Close() error { // Address implements constant.InboundListener func (t *Tunnel) Address() string { - return t.ttl.Address() + if t.ttl != nil { + return t.ttl.Address() + } + if t.tul != nil { + return t.tul.Address() + } + return "" } // Listen implements constant.InboundListener From 9df42d7b983b826f087e8612f5b8e10dbf9342d0 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 11 Dec 2022 09:25:46 +0800 Subject: [PATCH 219/250] fix: issue #292 --- adapter/outbound/hysteria.go | 29 ++++++++++++++++++++++------- adapter/outbound/tuic.go | 9 +++++++-- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index 56462399..c38946c8 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -9,6 +9,7 @@ import ( "encoding/pem" "fmt" "net" + "net/netip" "os" "regexp" "strconv" @@ -51,12 +52,13 @@ type Hysteria struct { func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { hdc := hyDialerWithContext{ ctx: context.Background(), - hyDialer: func() (net.PacketConn, error) { - return dialer.ListenPacket(ctx, "udp", "", h.Base.DialOptions(opts...)...) + hyDialer: func(network string) (net.PacketConn, error) { + return dialer.ListenPacket(ctx, network, "", h.Base.DialOptions(opts...)...) }, remoteAddr: func(addr string) (net.Addr, error) { return resolveUDPAddrWithPrefer(ctx, "udp", addr, h.prefer) }, + network: "udp", } tcpConn, err := h.client.DialTCP(metadata.RemoteAddress(), &hdc) @@ -70,12 +72,13 @@ func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata, opts . func (h *Hysteria) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { hdc := hyDialerWithContext{ ctx: context.Background(), - hyDialer: func() (net.PacketConn, error) { - return dialer.ListenPacket(ctx, "udp", "", h.Base.DialOptions(opts...)...) + hyDialer: func(network string) (net.PacketConn, error) { + return dialer.ListenPacket(ctx, network, "", h.Base.DialOptions(opts...)...) }, remoteAddr: func(addr string) (net.Addr, error) { return resolveUDPAddrWithPrefer(ctx, "udp", addr, h.prefer) }, + network: "udp", } udpConn, err := h.client.DialUDP(&hdc) if err != nil { @@ -325,13 +328,14 @@ func (c *hyPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) { } type hyDialerWithContext struct { - hyDialer func() (net.PacketConn, error) + hyDialer func(network string) (net.PacketConn, error) ctx context.Context remoteAddr func(host string) (net.Addr, error) + network string } func (h *hyDialerWithContext) ListenPacket() (net.PacketConn, error) { - return h.hyDialer() + return h.hyDialer(h.network) } func (h *hyDialerWithContext) Context() context.Context { @@ -339,5 +343,16 @@ func (h *hyDialerWithContext) Context() context.Context { } func (h *hyDialerWithContext) RemoteAddr(host string) (net.Addr, error) { - return h.remoteAddr(host) + addr, err := h.remoteAddr(host) + if err != nil { + return nil, err + } + if addrPort, err := netip.ParseAddrPort(addr.String()); err != nil { + if addrPort.Addr().Is6() { + h.network = "udp6" + } else { + h.network = "udp" + } + } + return addr, nil } diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index 5afaa895..8df1539a 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -74,11 +74,16 @@ func (t *Tuic) ListenPacketContext(ctx context.Context, metadata *C.Metadata, op } func (t *Tuic) dial(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) { - pc, err = dialer.ListenPacket(ctx, "udp", "", opts...) + udpAddr, err := resolveUDPAddrWithPrefer(ctx, "udp", t.addr, t.prefer) if err != nil { return nil, nil, err } - addr, err = resolveUDPAddrWithPrefer(ctx, "udp", t.addr, t.prefer) + addr = udpAddr + network := "udp" + if udpAddr.AddrPort().Addr().Is6() { + network = "udp6" + } + pc, err = dialer.ListenPacket(ctx, network, "", opts...) if err != nil { return nil, nil, err } From 57dfaf135d67126d01095fb15c87a60b7182c435 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 11 Dec 2022 13:41:44 +0800 Subject: [PATCH 220/250] fix: hysteria ipv6 outbound --- adapter/outbound/hysteria.go | 26 +++++++++----------------- transport/hysteria/conns/udp/hop.go | 12 ++++++------ transport/hysteria/transport/client.go | 9 +++++---- transport/hysteria/utils/misc.go | 2 +- 4 files changed, 21 insertions(+), 28 deletions(-) diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index c38946c8..9dc34b44 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -58,7 +58,6 @@ func (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata, opts . remoteAddr: func(addr string) (net.Addr, error) { return resolveUDPAddrWithPrefer(ctx, "udp", addr, h.prefer) }, - network: "udp", } tcpConn, err := h.client.DialTCP(metadata.RemoteAddress(), &hdc) @@ -78,7 +77,6 @@ func (h *Hysteria) ListenPacketContext(ctx context.Context, metadata *C.Metadata remoteAddr: func(addr string) (net.Addr, error) { return resolveUDPAddrWithPrefer(ctx, "udp", addr, h.prefer) }, - network: "udp", } udpConn, err := h.client.DialUDP(&hdc) if err != nil { @@ -331,11 +329,16 @@ type hyDialerWithContext struct { hyDialer func(network string) (net.PacketConn, error) ctx context.Context remoteAddr func(host string) (net.Addr, error) - network string } -func (h *hyDialerWithContext) ListenPacket() (net.PacketConn, error) { - return h.hyDialer(h.network) +func (h *hyDialerWithContext) ListenPacket(rAddr net.Addr) (net.PacketConn, error) { + network := "udp" + if addrPort, err := netip.ParseAddrPort(rAddr.String()); err == nil { + if addrPort.Addr().Is6() { + network = "udp6" + } + } + return h.hyDialer(network) } func (h *hyDialerWithContext) Context() context.Context { @@ -343,16 +346,5 @@ func (h *hyDialerWithContext) Context() context.Context { } func (h *hyDialerWithContext) RemoteAddr(host string) (net.Addr, error) { - addr, err := h.remoteAddr(host) - if err != nil { - return nil, err - } - if addrPort, err := netip.ParseAddrPort(addr.String()); err != nil { - if addrPort.Addr().Is6() { - h.network = "udp6" - } else { - h.network = "udp" - } - } - return addr, nil + return h.remoteAddr(host) } diff --git a/transport/hysteria/conns/udp/hop.go b/transport/hysteria/conns/udp/hop.go index e4958821..4097b692 100644 --- a/transport/hysteria/conns/udp/hop.go +++ b/transport/hysteria/conns/udp/hop.go @@ -90,7 +90,7 @@ func NewObfsUDPHopClientPacketConn(server string, hopInterval time.Duration, obf }, }, } - curConn, err := dialer.ListenPacket() + curConn, err := dialer.ListenPacket(ip) if err != nil { return nil, err } @@ -100,7 +100,7 @@ func NewObfsUDPHopClientPacketConn(server string, hopInterval time.Duration, obf conn.currentConn = curConn } go conn.recvRoutine(conn.currentConn) - go conn.hopRoutine(dialer) + go conn.hopRoutine(dialer, ip) return conn, nil } @@ -120,26 +120,26 @@ func (c *ObfsUDPHopClientPacketConn) recvRoutine(conn net.PacketConn) { } } -func (c *ObfsUDPHopClientPacketConn) hopRoutine(dialer utils.PacketDialer) { +func (c *ObfsUDPHopClientPacketConn) hopRoutine(dialer utils.PacketDialer, rAddr net.Addr) { ticker := time.NewTicker(c.hopInterval) defer ticker.Stop() for { select { case <-ticker.C: - c.hop(dialer) + c.hop(dialer, rAddr) case <-c.closeChan: return } } } -func (c *ObfsUDPHopClientPacketConn) hop(dialer utils.PacketDialer) { +func (c *ObfsUDPHopClientPacketConn) hop(dialer utils.PacketDialer, rAddr net.Addr) { c.connMutex.Lock() defer c.connMutex.Unlock() if c.closed { return } - newConn, err := dialer.ListenPacket() + newConn, err := dialer.ListenPacket(rAddr) if err != nil { // Skip this hop if failed to listen return diff --git a/transport/hysteria/transport/client.go b/transport/hysteria/transport/client.go index c30377a3..af0c3ea2 100644 --- a/transport/hysteria/transport/client.go +++ b/transport/hysteria/transport/client.go @@ -20,9 +20,10 @@ type ClientTransport struct { Dialer *net.Dialer } -func (ct *ClientTransport) quicPacketConn(proto string, server string, obfs obfsPkg.Obfuscator, hopInterval time.Duration, dialer utils.PacketDialer) (net.PacketConn, error) { +func (ct *ClientTransport) quicPacketConn(proto string, rAddr net.Addr, obfs obfsPkg.Obfuscator, hopInterval time.Duration, dialer utils.PacketDialer) (net.PacketConn, error) { + server := rAddr.String() if len(proto) == 0 || proto == "udp" { - conn, err := dialer.ListenPacket() + conn, err := dialer.ListenPacket(rAddr) if err != nil { return nil, err } @@ -39,7 +40,7 @@ func (ct *ClientTransport) quicPacketConn(proto string, server string, obfs obfs return conn, nil } } else if proto == "wechat-video" { - conn, err := dialer.ListenPacket() + conn, err := dialer.ListenPacket(rAddr) if err != nil { return nil, err } @@ -70,7 +71,7 @@ func (ct *ClientTransport) QUICDial(proto string, server string, tlsConfig *tls. return nil, err } - pktConn, err := ct.quicPacketConn(proto, serverUDPAddr.String(), obfs, hopInterval, dialer) + pktConn, err := ct.quicPacketConn(proto, serverUDPAddr, obfs, hopInterval, dialer) if err != nil { return nil, err } diff --git a/transport/hysteria/utils/misc.go b/transport/hysteria/utils/misc.go index 5d5159fc..670d737c 100644 --- a/transport/hysteria/utils/misc.go +++ b/transport/hysteria/utils/misc.go @@ -43,7 +43,7 @@ func last(s string, b byte) int { } type PacketDialer interface { - ListenPacket() (net.PacketConn, error) + ListenPacket(rAddr net.Addr) (net.PacketConn, error) Context() context.Context RemoteAddr(host string) (net.Addr, error) } From f657ac97f6a3c4ecdd91d8d6ab7d97f896e4a142 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 11 Dec 2022 15:48:37 +0800 Subject: [PATCH 221/250] fix: add an unmap before is6 --- adapter/outbound/hysteria.go | 2 +- adapter/outbound/tuic.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index 9dc34b44..cd36db5a 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -334,7 +334,7 @@ type hyDialerWithContext struct { func (h *hyDialerWithContext) ListenPacket(rAddr net.Addr) (net.PacketConn, error) { network := "udp" if addrPort, err := netip.ParseAddrPort(rAddr.String()); err == nil { - if addrPort.Addr().Is6() { + if addrPort.Addr().Unmap().Is6() { network = "udp6" } } diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index 8df1539a..5119f8d2 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -80,7 +80,7 @@ func (t *Tuic) dial(ctx context.Context, opts ...dialer.Option) (pc net.PacketCo } addr = udpAddr network := "udp" - if udpAddr.AddrPort().Addr().Is6() { + if udpAddr.AddrPort().Addr().Unmap().Is6() { network = "udp6" } pc, err = dialer.ListenPacket(ctx, network, "", opts...) From 02d3468516d13293e926c818758e9b83f9c464ee Mon Sep 17 00:00:00 2001 From: metacubex Date: Sun, 11 Dec 2022 18:24:55 +0800 Subject: [PATCH 222/250] chore: Android version supports child processes following the main process rules --- component/process/process_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/component/process/process_linux.go b/component/process/process_linux.go index d0853be9..9b1a4844 100644 --- a/component/process/process_linux.go +++ b/component/process/process_linux.go @@ -185,7 +185,7 @@ func splitCmdline(cmdline []byte) string { cmdline = bytes.Trim(cmdline, " ") idx := bytes.IndexFunc(cmdline, func(r rune) bool { - return unicode.IsControl(r) || unicode.IsSpace(r) + return unicode.IsControl(r) || unicode.IsSpace(r) || r == ':' }) if idx == -1 { From 8fa6bd1743604d16c0bedb87adeda621acf8c1d8 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 11 Dec 2022 23:53:39 +0800 Subject: [PATCH 223/250] fix: fix wireguard outbound not work with the sniffer --- adapter/outbound/wireguard.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index eb35ba03..002b65aa 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -212,7 +212,8 @@ func (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata, opts } conn, err = N.DialSerial(ctx, w.tunDevice, "tcp", M.ParseSocksaddr(metadata.RemoteAddress()), addrs) } else { - conn, err = w.tunDevice.DialContext(ctx, "tcp", M.ParseSocksaddr(metadata.Pure().RemoteAddress())) + port, _ := strconv.Atoi(metadata.DstPort) + conn, err = w.tunDevice.DialContext(ctx, "tcp", M.SocksaddrFrom(metadata.DstIP, uint16(port))) } if err != nil { return nil, err @@ -242,7 +243,8 @@ func (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadat } metadata.DstIP = ip } - pc, err = w.tunDevice.ListenPacket(ctx, M.ParseSocksaddr(metadata.Pure().RemoteAddress())) + port, _ := strconv.Atoi(metadata.DstPort) + pc, err = w.tunDevice.ListenPacket(ctx, M.SocksaddrFrom(metadata.DstIP, uint16(port))) if err != nil { return nil, err } From 1333f1fd47830c0faf1e3b83eb7367265c7f68a9 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 13 Dec 2022 08:23:17 +0800 Subject: [PATCH 224/250] fix: fix wireguard outbound not work with ipv6 server --- adapter/outbound/wireguard.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 002b65aa..22eca388 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -91,8 +91,7 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { reserved[1] = uint8(option.Reserved[1]) reserved[2] = uint8(option.Reserved[2]) } - peerAddr := M.ParseSocksaddr(option.Server) - peerAddr.Port = uint16(option.Port) + peerAddr := M.ParseSocksaddrHostPort(option.Server, uint16(option.Port)) outbound.bind = wireguard.NewClientBind(context.Background(), outbound.dialer, peerAddr, reserved) localPrefixes := make([]netip.Prefix, 0, 2) if len(option.Ip) > 0 { From f87144f84b29017806bb9458a2af907010526d1a Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 13 Dec 2022 08:35:01 +0800 Subject: [PATCH 225/250] chore: add persistent-keepalive for wireguard --- adapter/outbound/wireguard.go | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 22eca388..75cb6f9f 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -41,18 +41,19 @@ type WireGuard struct { type WireGuardOption struct { BasicOption - Name string `proxy:"name"` - Server string `proxy:"server"` - Port int `proxy:"port"` - Ip string `proxy:"ip,omitempty"` - Ipv6 string `proxy:"ipv6,omitempty"` - PrivateKey string `proxy:"private-key"` - PublicKey string `proxy:"public-key"` - PreSharedKey string `proxy:"pre-shared-key,omitempty"` - Reserved []int `proxy:"reserved,omitempty"` - Workers int `proxy:"workers,omitempty"` - MTU int `proxy:"mtu,omitempty"` - UDP bool `proxy:"udp,omitempty"` + Name string `proxy:"name"` + Server string `proxy:"server"` + Port int `proxy:"port"` + Ip string `proxy:"ip,omitempty"` + Ipv6 string `proxy:"ipv6,omitempty"` + PrivateKey string `proxy:"private-key"` + PublicKey string `proxy:"public-key"` + PreSharedKey string `proxy:"pre-shared-key,omitempty"` + Reserved []int `proxy:"reserved,omitempty"` + Workers int `proxy:"workers,omitempty"` + MTU int `proxy:"mtu,omitempty"` + UDP bool `proxy:"udp,omitempty"` + PersistentKeepalive int `proxy:"persistent-keepalive,omitempty"` } type wgDialer struct { @@ -159,6 +160,9 @@ func NewWireGuard(option WireGuardOption) (*WireGuard, error) { if has6 { ipcConf += "\nallowed_ip=::/0" } + if option.PersistentKeepalive != 0 { + ipcConf += fmt.Sprintf("\npersistent_keepalive_interval=%d", option.PersistentKeepalive) + } mtu := option.MTU if mtu == 0 { mtu = 1408 From 88acf8e098fead3c53e0ae7a060ef71d10dd1a19 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 13 Dec 2022 11:18:32 +0800 Subject: [PATCH 226/250] fix: fix bindIfaceToListenConfig() in windows force bind to an ipv4 address --- adapter/outbound/direct.go | 2 +- adapter/outbound/hysteria.go | 4 +--- adapter/outbound/shadowsocks.go | 5 ++--- adapter/outbound/shadowsocksr.go | 5 ++--- adapter/outbound/socks5.go | 26 +++++++++++++------------- adapter/outbound/tuic.go | 6 +----- adapter/outbound/wireguard.go | 2 +- component/dialer/dialer.go | 12 ++++++++++++ dns/util.go | 2 +- 9 files changed, 34 insertions(+), 30 deletions(-) diff --git a/adapter/outbound/direct.go b/adapter/outbound/direct.go index d7ffd478..cf1b2648 100644 --- a/adapter/outbound/direct.go +++ b/adapter/outbound/direct.go @@ -27,7 +27,7 @@ func (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata, opts ... // ListenPacketContext implements C.ProxyAdapter func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { opts = append(opts, dialer.WithResolver(resolver.DefaultResolver)) - pc, err := dialer.ListenPacket(ctx, "udp", "", d.Base.DialOptions(opts...)...) + pc, err := dialer.ListenPacket(ctx, dialer.ParseNetwork("udp", metadata.DstIP), "", d.Base.DialOptions(opts...)...) if err != nil { return nil, err } diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index cd36db5a..e019f7fa 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -334,9 +334,7 @@ type hyDialerWithContext struct { func (h *hyDialerWithContext) ListenPacket(rAddr net.Addr) (net.PacketConn, error) { network := "udp" if addrPort, err := netip.ParseAddrPort(rAddr.String()); err == nil { - if addrPort.Addr().Unmap().Is6() { - network = "udp6" - } + network = dialer.ParseNetwork(network, addrPort.Addr()) } return h.hyDialer(network) } diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 39c02b91..74400301 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -105,14 +105,13 @@ func (ss *ShadowSocks) ListenPacketContext(ctx context.Context, metadata *C.Meta } return newPacketConn(uot.NewClientConn(tcpConn), ss), nil } - pc, err := dialer.ListenPacket(ctx, "udp", "", ss.Base.DialOptions(opts...)...) + addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ss.addr, ss.prefer) if err != nil { return nil, err } - addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ss.addr, ss.prefer) + pc, err := dialer.ListenPacket(ctx, dialer.ParseNetwork("udp", addr.AddrPort().Addr()), "", ss.Base.DialOptions(opts...)...) if err != nil { - pc.Close() return nil, err } pc = ss.method.DialPacketConn(&bufio.BindPacketConn{PacketConn: pc, Addr: addr}) diff --git a/adapter/outbound/shadowsocksr.go b/adapter/outbound/shadowsocksr.go index 8bc9ef65..5d71ed65 100644 --- a/adapter/outbound/shadowsocksr.go +++ b/adapter/outbound/shadowsocksr.go @@ -74,14 +74,13 @@ func (ssr *ShadowSocksR) DialContext(ctx context.Context, metadata *C.Metadata, // ListenPacketContext implements C.ProxyAdapter func (ssr *ShadowSocksR) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { - pc, err := dialer.ListenPacket(ctx, "udp", "", ssr.Base.DialOptions(opts...)...) + addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ssr.addr, ssr.prefer) if err != nil { return nil, err } - addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ssr.addr, ssr.prefer) + pc, err := dialer.ListenPacket(ctx, dialer.ParseNetwork("udp", addr.AddrPort().Addr()), "", ssr.Base.DialOptions(opts...)...) if err != nil { - pc.Close() return nil, err } diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go index 63b76dd3..0da864b6 100644 --- a/adapter/outbound/socks5.go +++ b/adapter/outbound/socks5.go @@ -114,19 +114,6 @@ func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata, return } - pc, err := dialer.ListenPacket(ctx, "udp", "", ss.Base.DialOptions(opts...)...) - if err != nil { - return - } - - go func() { - io.Copy(io.Discard, c) - c.Close() - // A UDP association terminates when the TCP connection that the UDP - // ASSOCIATE request arrived on terminates. RFC1928 - pc.Close() - }() - // Support unspecified UDP bind address. bindUDPAddr := bindAddr.UDPAddr() if bindUDPAddr == nil { @@ -141,6 +128,19 @@ func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata, bindUDPAddr.IP = serverAddr.IP } + pc, err := dialer.ListenPacket(ctx, dialer.ParseNetwork("udp", bindUDPAddr.AddrPort().Addr()), "", ss.Base.DialOptions(opts...)...) + if err != nil { + return + } + + go func() { + io.Copy(io.Discard, c) + c.Close() + // A UDP association terminates when the TCP connection that the UDP + // ASSOCIATE request arrived on terminates. RFC1928 + pc.Close() + }() + return newPacketConn(&socksPacketConn{PacketConn: pc, rAddr: bindUDPAddr, tcpConn: c}, ss), nil } diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index 5119f8d2..fa171187 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -79,11 +79,7 @@ func (t *Tuic) dial(ctx context.Context, opts ...dialer.Option) (pc net.PacketCo return nil, nil, err } addr = udpAddr - network := "udp" - if udpAddr.AddrPort().Addr().Unmap().Is6() { - network = "udp6" - } - pc, err = dialer.ListenPacket(ctx, network, "", opts...) + pc, err = dialer.ListenPacket(ctx, dialer.ParseNetwork("udp", udpAddr.AddrPort().Addr()), "", opts...) if err != nil { return nil, nil, err } diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index 75cb6f9f..ad7a157f 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -65,7 +65,7 @@ func (d *wgDialer) DialContext(ctx context.Context, network string, destination } func (d *wgDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { - return dialer.ListenPacket(ctx, "udp", "", d.options...) + return dialer.ListenPacket(ctx, dialer.ParseNetwork("udp", destination.Addr), "", d.options...) } func NewWireGuard(option WireGuardOption) (*WireGuard, error) { diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 44fa2e90..c6b57d6c 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -6,6 +6,7 @@ import ( "fmt" "net" "net/netip" + "runtime" "strings" "sync" @@ -24,6 +25,17 @@ var ( ErrorDisableIPv6 = errors.New("IPv6 is disabled, dialer cancel") ) +func ParseNetwork(network string, addr netip.Addr) string { + if runtime.GOOS == "windows" { // fix bindIfaceToListenConfig() in windows force bind to an ipv4 address + if !strings.HasSuffix(network, "4") && + !strings.HasSuffix(network, "6") && + addr.Unmap().Is6() { + network += "6" + } + } + return network +} + func ApplyOptions(options ...Option) *option { opt := &option{ interfaceName: DefaultInterface.Load(), diff --git a/dns/util.go b/dns/util.go index fb42a9e7..f6b9c090 100644 --- a/dns/util.go +++ b/dns/util.go @@ -207,7 +207,7 @@ func dialContextExtra(ctx context.Context, adapterName string, network string, a DstPort: port, } if !ok { - packetConn, err := dialer.ListenPacket(ctx, network, metadata.RemoteAddress(), opts...) + packetConn, err := dialer.ListenPacket(ctx, dialer.ParseNetwork(network, dstIP), "", opts...) if err != nil { return nil, err } From b3b5f17e039004f92ca970f8387227c0a635bdbe Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 13 Dec 2022 11:23:34 +0800 Subject: [PATCH 227/250] chore: cleanup doh/doq's code --- dns/doh.go | 20 ++++++-------------- dns/doq.go | 22 ++++++---------------- 2 files changed, 12 insertions(+), 30 deletions(-) diff --git a/dns/doh.go b/dns/doh.go index ac5d7486..d2bfeb82 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -15,7 +15,6 @@ import ( "sync" "time" - "github.com/Dreamacro/clash/component/dialer" tlsC "github.com/Dreamacro/clash/component/tls" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" @@ -530,21 +529,14 @@ func (doh *dnsOverHTTPS) dialQuic(ctx context.Context, addr string, tlsCfg *tls. Port: portInt, } var conn net.PacketConn - if doh.proxyAdapter == "" { - conn, err = dialer.ListenPacket(ctx, "udp", "") - if err != nil { - return nil, err + if wrapConn, err := dialContextExtra(ctx, doh.proxyAdapter, "udp", addr, doh.r); err == nil { + if pc, ok := wrapConn.(*wrapPacketConn); ok { + conn = pc + } else { + return nil, fmt.Errorf("conn isn't wrapPacketConn") } } else { - if wrapConn, err := dialContextExtra(ctx, doh.proxyAdapter, "udp", addr, doh.r); err == nil { - if pc, ok := wrapConn.(*wrapPacketConn); ok { - conn = pc - } else { - return nil, fmt.Errorf("conn isn't wrapPacketConn") - } - } else { - return nil, err - } + return nil, err } return quic.DialEarlyContext(ctx, conn, &udpAddr, doh.url.Host, tlsCfg, cfg) } diff --git a/dns/doq.go b/dns/doq.go index 16a32a3a..3f2b7d07 100644 --- a/dns/doq.go +++ b/dns/doq.go @@ -12,7 +12,6 @@ import ( "sync" "time" - "github.com/Dreamacro/clash/component/dialer" tlsC "github.com/Dreamacro/clash/component/tls" "github.com/metacubex/quic-go" @@ -336,23 +335,14 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connectio p, err := strconv.Atoi(port) udpAddr := net.UDPAddr{IP: net.ParseIP(ip), Port: p} var udp net.PacketConn - if doq.proxyAdapter == "" { - udp, err = dialer.ListenPacket(ctx, "udp", "") - if err != nil { - return nil, err - } - } else { - conn, err := dialContextExtra(ctx, doq.proxyAdapter, "udp", addr, doq.r) - if err != nil { - return nil, err - } - - wrapConn, ok := conn.(*wrapPacketConn) - if !ok { + if wrapConn, err := dialContextExtra(ctx, doq.proxyAdapter, "udp", addr, doq.r); err == nil { + if pc, ok := wrapConn.(*wrapPacketConn); ok { + udp = pc + } else { return nil, fmt.Errorf("quic create packet failed") } - - udp = wrapConn + } else { + return nil, err } host, _, err := net.SplitHostPort(doq.addr) From 17cbbb5bf076c45fcaa1d32f63f99e0e684fee78 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 13 Dec 2022 12:38:46 +0800 Subject: [PATCH 228/250] chore: split dns's dialContext and listenPacket --- dns/doh.go | 22 ++-------- dns/doq.go | 22 ++-------- dns/util.go | 124 ++++++++++++++++++++++++++++++---------------------- 3 files changed, 78 insertions(+), 90 deletions(-) diff --git a/dns/doh.go b/dns/doh.go index d2bfeb82..34685578 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -528,14 +528,8 @@ func (doh *dnsOverHTTPS) dialQuic(ctx context.Context, addr string, tlsCfg *tls. IP: net.ParseIP(ip), Port: portInt, } - var conn net.PacketConn - if wrapConn, err := dialContextExtra(ctx, doh.proxyAdapter, "udp", addr, doh.r); err == nil { - if pc, ok := wrapConn.(*wrapPacketConn); ok { - conn = pc - } else { - return nil, fmt.Errorf("conn isn't wrapPacketConn") - } - } else { + conn, err := listenPacket(ctx, doh.proxyAdapter, "udp", addr, doh.r) + if err != nil { return nil, err } return quic.DialEarlyContext(ctx, conn, &udpAddr, doh.url.Host, tlsCfg, cfg) @@ -556,20 +550,10 @@ func (doh *dnsOverHTTPS) probeH3( if err != nil { return "", fmt.Errorf("failed to dial: %w", err) } + addr = rawConn.RemoteAddr().String() // It's never actually used. _ = rawConn.Close() - udpConn, ok := rawConn.(*net.UDPConn) - if !ok { - if packetConn, ok := rawConn.(*wrapPacketConn); !ok { - return "", fmt.Errorf("not a UDP connection to %s", doh.Address()) - } else { - addr = packetConn.RemoteAddr().String() - } - } else { - addr = udpConn.RemoteAddr().String() - } - // Avoid spending time on probing if this upstream only supports HTTP/3. if doh.supportsH3() && !doh.supportsHTTP() { return addr, nil diff --git a/dns/doq.go b/dns/doq.go index 3f2b7d07..1c5956af 100644 --- a/dns/doq.go +++ b/dns/doq.go @@ -313,19 +313,9 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connectio if err != nil { return nil, fmt.Errorf("failed to open a QUIC connection: %w", err) } + addr := rawConn.RemoteAddr().String() // It's never actually used _ = rawConn.Close() - var addr string - udpConn, ok := rawConn.(*net.UDPConn) - if !ok { - if packetConn, ok := rawConn.(*wrapPacketConn); !ok { - return nil, fmt.Errorf("failed to open connection to %s", doq.addr) - } else { - addr = packetConn.RemoteAddr().String() - } - } else { - addr = udpConn.RemoteAddr().String() - } ip, port, err := net.SplitHostPort(addr) if err != nil { @@ -334,14 +324,8 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connectio p, err := strconv.Atoi(port) udpAddr := net.UDPAddr{IP: net.ParseIP(ip), Port: p} - var udp net.PacketConn - if wrapConn, err := dialContextExtra(ctx, doq.proxyAdapter, "udp", addr, doq.r); err == nil { - if pc, ok := wrapConn.(*wrapPacketConn); ok { - udp = pc - } else { - return nil, fmt.Errorf("quic create packet failed") - } - } else { + udp, err := listenPacket(ctx, doq.proxyAdapter, "udp", addr, doq.r) + if err != nil { return nil, err } diff --git a/dns/util.go b/dns/util.go index f6b9c090..dfd2bafd 100644 --- a/dns/util.go +++ b/dns/util.go @@ -168,70 +168,90 @@ func getDialHandler(r *Resolver, proxyAdapter string, opts ...dialer.Option) dia opts = append(opts, dialer.WithResolver(r)) return dialer.DialContext(ctx, network, addr, opts...) } else { - return dialContextExtra(ctx, proxyAdapter, network, addr, r, opts...) + host, port, err := net.SplitHostPort(addr) + if err != nil { + return nil, err + } + adapter, ok := tunnel.Proxies()[proxyAdapter] + if !ok { + opts = append(opts, dialer.WithInterface(proxyAdapter)) + } + if strings.Contains(network, "tcp") { + // tcp can resolve host by remote + metadata := &C.Metadata{ + NetWork: C.TCP, + Host: host, + DstPort: port, + } + if ok { + return adapter.DialContext(ctx, metadata, opts...) + } + opts = append(opts, dialer.WithResolver(r)) + return dialer.DialContext(ctx, network, addr, opts...) + } else { + // udp must resolve host first + dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r) + if err != nil { + return nil, err + } + metadata := &C.Metadata{ + NetWork: C.UDP, + Host: "", + DstIP: dstIP, + DstPort: port, + } + if !ok { + return dialer.DialContext(ctx, network, addr, opts...) + } + + if !adapter.SupportUDP() { + return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter) + } + + packetConn, err := adapter.ListenPacketContext(ctx, metadata, opts...) + if err != nil { + return nil, err + } + + return &wrapPacketConn{ + PacketConn: packetConn, + rAddr: metadata.UDPAddr(), + }, nil + } } } } -func dialContextExtra(ctx context.Context, adapterName string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.Conn, error) { +func listenPacket(ctx context.Context, proxyAdapter string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.PacketConn, error) { host, port, err := net.SplitHostPort(addr) if err != nil { return nil, err } - adapter, ok := tunnel.Proxies()[adapterName] + adapter, ok := tunnel.Proxies()[proxyAdapter] + if !ok && len(proxyAdapter) != 0 { + opts = append(opts, dialer.WithInterface(proxyAdapter)) + } + + // udp must resolve host first + dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r) + if err != nil { + return nil, err + } + metadata := &C.Metadata{ + NetWork: C.UDP, + Host: "", + DstIP: dstIP, + DstPort: port, + } if !ok { - opts = append(opts, dialer.WithInterface(adapterName)) + return dialer.ListenPacket(ctx, dialer.ParseNetwork(network, dstIP), "", opts...) } - if strings.Contains(network, "tcp") { - // tcp can resolve host by remote - metadata := &C.Metadata{ - NetWork: C.TCP, - Host: host, - DstPort: port, - } - if ok { - return adapter.DialContext(ctx, metadata, opts...) - } - opts = append(opts, dialer.WithResolver(r)) - return dialer.DialContext(ctx, network, addr, opts...) - } else { - // udp must resolve host first - dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r) - if err != nil { - return nil, err - } - metadata := &C.Metadata{ - NetWork: C.UDP, - Host: "", - DstIP: dstIP, - DstPort: port, - } - if !ok { - packetConn, err := dialer.ListenPacket(ctx, dialer.ParseNetwork(network, dstIP), "", opts...) - if err != nil { - return nil, err - } - return &wrapPacketConn{ - PacketConn: packetConn, - rAddr: metadata.UDPAddr(), - }, nil - } - - if !adapter.SupportUDP() { - return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", adapterName) - } - - packetConn, err := adapter.ListenPacketContext(ctx, metadata, opts...) - if err != nil { - return nil, err - } - - return &wrapPacketConn{ - PacketConn: packetConn, - rAddr: metadata.UDPAddr(), - }, nil + if !adapter.SupportUDP() { + return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter) } + + return adapter.ListenPacketContext(ctx, metadata, opts...) } func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) { From bffb0573a65d33397cdb7dddfc743e2b923517b7 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 13 Dec 2022 13:20:40 +0800 Subject: [PATCH 229/250] fix: safeConnClose not working --- adapter/outbound/http.go | 4 +++- adapter/outbound/shadowsocks.go | 4 +++- adapter/outbound/shadowsocksr.go | 4 +++- adapter/outbound/snell.go | 4 +++- adapter/outbound/socks5.go | 8 ++++++-- adapter/outbound/trojan.go | 12 +++++++++--- adapter/outbound/vless.go | 16 ++++++++++++---- adapter/outbound/vmess.go | 16 ++++++++++++---- 8 files changed, 51 insertions(+), 17 deletions(-) diff --git a/adapter/outbound/http.go b/adapter/outbound/http.go index 67cafd7e..bdcf5c9f 100644 --- a/adapter/outbound/http.go +++ b/adapter/outbound/http.go @@ -67,7 +67,9 @@ func (h *Http) DialContext(ctx context.Context, metadata *C.Metadata, opts ...di } tcpKeepAlive(c) - defer safeConnClose(c, err) + defer func() { + safeConnClose(c, err) + }() c, err = h.StreamConn(c, metadata) if err != nil { diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 74400301..263284b0 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -90,7 +90,9 @@ func (ss *ShadowSocks) DialContext(ctx context.Context, metadata *C.Metadata, op } tcpKeepAlive(c) - defer safeConnClose(c, err) + defer func() { + safeConnClose(c, err) + }() c, err = ss.StreamConn(c, metadata) return NewConn(c, ss), err diff --git a/adapter/outbound/shadowsocksr.go b/adapter/outbound/shadowsocksr.go index 5d71ed65..c4112e6e 100644 --- a/adapter/outbound/shadowsocksr.go +++ b/adapter/outbound/shadowsocksr.go @@ -66,7 +66,9 @@ func (ssr *ShadowSocksR) DialContext(ctx context.Context, metadata *C.Metadata, } tcpKeepAlive(c) - defer safeConnClose(c, err) + defer func() { + safeConnClose(c, err) + }() c, err = ssr.StreamConn(c, metadata) return NewConn(c, ssr), err diff --git a/adapter/outbound/snell.go b/adapter/outbound/snell.go index 0aadb1c8..f2bc6116 100644 --- a/adapter/outbound/snell.go +++ b/adapter/outbound/snell.go @@ -84,7 +84,9 @@ func (s *Snell) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d } tcpKeepAlive(c) - defer safeConnClose(c, err) + defer func() { + safeConnClose(c, err) + }() c, err = s.StreamConn(c, metadata) return NewConn(c, s), err diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go index 0da864b6..fe3b9bdc 100644 --- a/adapter/outbound/socks5.go +++ b/adapter/outbound/socks5.go @@ -71,7 +71,9 @@ func (ss *Socks5) DialContext(ctx context.Context, metadata *C.Metadata, opts .. } tcpKeepAlive(c) - defer safeConnClose(c, err) + defer func() { + safeConnClose(c, err) + }() c, err = ss.StreamConn(c, metadata) if err != nil { @@ -97,7 +99,9 @@ func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata, c = cc } - defer safeConnClose(c, err) + defer func() { + safeConnClose(c, err) + }() tcpKeepAlive(c) var user *socks5.User diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index 946a0101..9d3dcd4c 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -127,7 +127,9 @@ func (t *Trojan) DialContext(ctx context.Context, metadata *C.Metadata, opts ... } tcpKeepAlive(c) - defer safeConnClose(c, err) + defer func() { + safeConnClose(c, err) + }() c, err = t.StreamConn(c, metadata) if err != nil { @@ -147,13 +149,17 @@ func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata, if err != nil { return nil, fmt.Errorf("%s connect error: %w", t.addr, err) } - defer safeConnClose(c, err) + defer func() { + safeConnClose(c, err) + }() } else { c, err = dialer.DialContext(ctx, "tcp", t.addr, t.Base.DialOptions(opts...)...) if err != nil { return nil, fmt.Errorf("%s connect error: %w", t.addr, err) } - defer safeConnClose(c, err) + defer func() { + safeConnClose(c, err) + }() tcpKeepAlive(c) c, err = t.plainStream(c) if err != nil { diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index b86a9d6f..f1e62829 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -208,7 +208,9 @@ func (v *Vless) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d if err != nil { return nil, err } - defer safeConnClose(c, err) + defer func() { + safeConnClose(c, err) + }() c, err = v.client.StreamConn(c, parseVlessAddr(metadata)) if err != nil { @@ -223,7 +225,9 @@ func (v *Vless) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } tcpKeepAlive(c) - defer safeConnClose(c, err) + defer func() { + safeConnClose(c, err) + }() c, err = v.StreamConn(c, metadata) return NewConn(c, v), err @@ -247,7 +251,9 @@ func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o if err != nil { return nil, err } - defer safeConnClose(c, err) + defer func() { + safeConnClose(c, err) + }() c, err = v.client.StreamConn(c, parseVlessAddr(metadata)) } else { @@ -256,7 +262,9 @@ func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } tcpKeepAlive(c) - defer safeConnClose(c, err) + defer func() { + safeConnClose(c, err) + }() c, err = v.StreamConn(c, metadata) } diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index 6d135b62..65d3073b 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -221,7 +221,9 @@ func (v *Vmess) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d if err != nil { return nil, err } - defer safeConnClose(c, err) + defer func() { + safeConnClose(c, err) + }() c, err = v.client.DialConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) if err != nil { @@ -236,7 +238,9 @@ func (v *Vmess) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } tcpKeepAlive(c) - defer safeConnClose(c, err) + defer func() { + safeConnClose(c, err) + }() c, err = v.StreamConn(c, metadata) return NewConn(c, v), err @@ -267,7 +271,9 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o if err != nil { return nil, err } - defer safeConnClose(c, err) + defer func() { + safeConnClose(c, err) + }() if v.option.XUDP { c, err = v.client.DialXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) @@ -280,7 +286,9 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } tcpKeepAlive(c) - defer safeConnClose(c, err) + defer func() { + safeConnClose(c, err) + }() c, err = v.StreamConn(c, metadata) } From 9711390c1869a7cd60acd1249a37ca1bc67d5c41 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 13 Dec 2022 18:09:19 +0800 Subject: [PATCH 230/250] fix: check conn is nil in safeConnClose --- adapter/outbound/util.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adapter/outbound/util.go b/adapter/outbound/util.go index f32aae29..68d6b355 100644 --- a/adapter/outbound/util.go +++ b/adapter/outbound/util.go @@ -126,7 +126,7 @@ func resolveUDPAddrWithPrefer(ctx context.Context, network, address string, pref if !ip.IsValid() && fallback.IsValid() { ip = fallback } - + if err != nil { return nil, err } @@ -134,7 +134,7 @@ func resolveUDPAddrWithPrefer(ctx context.Context, network, address string, pref } func safeConnClose(c net.Conn, err error) { - if err != nil { + if err != nil && c != nil { _ = c.Close() } } From afb2364ca28d532dd57343b65fbfd27c153a2e6e Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 13 Dec 2022 20:40:07 +0800 Subject: [PATCH 231/250] chore: wireguard's reserved support base64 input --- adapter/outbound/wireguard.go | 26 +++++++++---------- common/structure/structure.go | 47 +++++++++++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/adapter/outbound/wireguard.go b/adapter/outbound/wireguard.go index ad7a157f..d1a5ea6e 100644 --- a/adapter/outbound/wireguard.go +++ b/adapter/outbound/wireguard.go @@ -41,19 +41,19 @@ type WireGuard struct { type WireGuardOption struct { BasicOption - Name string `proxy:"name"` - Server string `proxy:"server"` - Port int `proxy:"port"` - Ip string `proxy:"ip,omitempty"` - Ipv6 string `proxy:"ipv6,omitempty"` - PrivateKey string `proxy:"private-key"` - PublicKey string `proxy:"public-key"` - PreSharedKey string `proxy:"pre-shared-key,omitempty"` - Reserved []int `proxy:"reserved,omitempty"` - Workers int `proxy:"workers,omitempty"` - MTU int `proxy:"mtu,omitempty"` - UDP bool `proxy:"udp,omitempty"` - PersistentKeepalive int `proxy:"persistent-keepalive,omitempty"` + Name string `proxy:"name"` + Server string `proxy:"server"` + Port int `proxy:"port"` + Ip string `proxy:"ip,omitempty"` + Ipv6 string `proxy:"ipv6,omitempty"` + PrivateKey string `proxy:"private-key"` + PublicKey string `proxy:"public-key"` + PreSharedKey string `proxy:"pre-shared-key,omitempty"` + Reserved []uint8 `proxy:"reserved,omitempty"` + Workers int `proxy:"workers,omitempty"` + MTU int `proxy:"mtu,omitempty"` + UDP bool `proxy:"udp,omitempty"` + PersistentKeepalive int `proxy:"persistent-keepalive,omitempty"` } type wgDialer struct { diff --git a/common/structure/structure.go b/common/structure/structure.go index c2ae393d..88d7df2e 100644 --- a/common/structure/structure.go +++ b/common/structure/structure.go @@ -3,6 +3,7 @@ package structure // references: https://github.com/mitchellh/mapstructure import ( + "encoding/base64" "fmt" "reflect" "strconv" @@ -86,8 +87,10 @@ func (d *Decoder) Decode(src map[string]any, dst any) error { func (d *Decoder) decode(name string, data any, val reflect.Value) error { switch val.Kind() { - case reflect.Int: + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return d.decodeInt(name, data, val) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return d.decodeUint(name, data, val) case reflect.String: return d.decodeString(name, data, val) case reflect.Bool: @@ -109,8 +112,10 @@ func (d *Decoder) decodeInt(name string, data any, val reflect.Value) (err error dataVal := reflect.ValueOf(data) kind := dataVal.Kind() switch { - case kind == reflect.Int: + case kind == reflect.Int || kind == reflect.Int8 || kind == reflect.Int16 || kind == reflect.Int32 || kind == reflect.Int64: val.SetInt(dataVal.Int()) + case (kind == reflect.Uint || kind == reflect.Uint8 || kind == reflect.Uint16 || kind == reflect.Uint32 || kind == reflect.Uint64) && d.option.WeaklyTypedInput: + val.SetInt(int64(dataVal.Uint())) case kind == reflect.Float64 && d.option.WeaklyTypedInput: val.SetInt(int64(dataVal.Float())) case kind == reflect.String && d.option.WeaklyTypedInput: @@ -130,6 +135,33 @@ func (d *Decoder) decodeInt(name string, data any, val reflect.Value) (err error return err } +func (d *Decoder) decodeUint(name string, data any, val reflect.Value) (err error) { + dataVal := reflect.ValueOf(data) + kind := dataVal.Kind() + switch { + case kind == reflect.Uint || kind == reflect.Uint8 || kind == reflect.Uint16 || kind == reflect.Uint32 || kind == reflect.Uint64: + val.SetUint(dataVal.Uint()) + case (kind == reflect.Int || kind == reflect.Int8 || kind == reflect.Int16 || kind == reflect.Int32 || kind == reflect.Int64) && d.option.WeaklyTypedInput: + val.SetUint(uint64(dataVal.Int())) + case kind == reflect.Float64 && d.option.WeaklyTypedInput: + val.SetUint(uint64(int64(dataVal.Float()))) + case kind == reflect.String && d.option.WeaklyTypedInput: + var i uint64 + i, err = strconv.ParseUint(dataVal.String(), 0, val.Type().Bits()) + if err == nil { + val.SetUint(i) + } else { + err = fmt.Errorf("cannot parse '%s' as int: %s", name, err) + } + default: + err = fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%s'", + name, val.Type(), dataVal.Type(), + ) + } + return err +} + func (d *Decoder) decodeString(name string, data any, val reflect.Value) (err error) { dataVal := reflect.ValueOf(data) kind := dataVal.Kind() @@ -169,6 +201,17 @@ func (d *Decoder) decodeSlice(name string, data any, val reflect.Value) error { valType := val.Type() valElemType := valType.Elem() + if dataVal.Kind() == reflect.String && valElemType.Kind() == reflect.Uint8 { + s := []byte(dataVal.String()) + b := make([]byte, base64.StdEncoding.DecodedLen(len(s))) + n, err := base64.StdEncoding.Decode(b, s) + if err != nil { + return fmt.Errorf("try decode '%s' by base64 error: %w", name, err) + } + val.SetBytes(b[:n]) + return nil + } + if dataVal.Kind() != reflect.Slice { return fmt.Errorf("'%s' is not a slice", name) } From 8a2d1ec5a75ad241459e0fc40c55b15dc5e6e0ba Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 13 Dec 2022 21:13:31 +0800 Subject: [PATCH 232/250] chore: better structure decoder --- common/structure/structure.go | 91 ++++++++++++++++++++++++++++++----- 1 file changed, 78 insertions(+), 13 deletions(-) diff --git a/common/structure/structure.go b/common/structure/structure.go index 88d7df2e..78f344a4 100644 --- a/common/structure/structure.go +++ b/common/structure/structure.go @@ -86,11 +86,16 @@ func (d *Decoder) Decode(src map[string]any, dst any) error { } func (d *Decoder) decode(name string, data any, val reflect.Value) error { - switch val.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + kind := val.Kind() + switch { + case isInt(kind): return d.decodeInt(name, data, val) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + case isUint(kind): return d.decodeUint(name, data, val) + case isFloat(kind): + return d.decodeFloat(name, data, val) + } + switch kind { case reflect.String: return d.decodeString(name, data, val) case reflect.Bool: @@ -108,15 +113,42 @@ func (d *Decoder) decode(name string, data any, val reflect.Value) error { } } +func isInt(kind reflect.Kind) bool { + switch kind { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return true + default: + return false + } +} + +func isUint(kind reflect.Kind) bool { + switch kind { + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return true + default: + return false + } +} + +func isFloat(kind reflect.Kind) bool { + switch kind { + case reflect.Float32, reflect.Float64: + return true + default: + return false + } +} + func (d *Decoder) decodeInt(name string, data any, val reflect.Value) (err error) { dataVal := reflect.ValueOf(data) kind := dataVal.Kind() switch { - case kind == reflect.Int || kind == reflect.Int8 || kind == reflect.Int16 || kind == reflect.Int32 || kind == reflect.Int64: + case isInt(kind): val.SetInt(dataVal.Int()) - case (kind == reflect.Uint || kind == reflect.Uint8 || kind == reflect.Uint16 || kind == reflect.Uint32 || kind == reflect.Uint64) && d.option.WeaklyTypedInput: + case isUint(kind) && d.option.WeaklyTypedInput: val.SetInt(int64(dataVal.Uint())) - case kind == reflect.Float64 && d.option.WeaklyTypedInput: + case isFloat(kind) && d.option.WeaklyTypedInput: val.SetInt(int64(dataVal.Float())) case kind == reflect.String && d.option.WeaklyTypedInput: var i int64 @@ -139,12 +171,12 @@ func (d *Decoder) decodeUint(name string, data any, val reflect.Value) (err erro dataVal := reflect.ValueOf(data) kind := dataVal.Kind() switch { - case kind == reflect.Uint || kind == reflect.Uint8 || kind == reflect.Uint16 || kind == reflect.Uint32 || kind == reflect.Uint64: + case isUint(kind): val.SetUint(dataVal.Uint()) - case (kind == reflect.Int || kind == reflect.Int8 || kind == reflect.Int16 || kind == reflect.Int32 || kind == reflect.Int64) && d.option.WeaklyTypedInput: + case isInt(kind) && d.option.WeaklyTypedInput: val.SetUint(uint64(dataVal.Int())) - case kind == reflect.Float64 && d.option.WeaklyTypedInput: - val.SetUint(uint64(int64(dataVal.Float()))) + case isFloat(kind) && d.option.WeaklyTypedInput: + val.SetUint(uint64(dataVal.Float())) case kind == reflect.String && d.option.WeaklyTypedInput: var i uint64 i, err = strconv.ParseUint(dataVal.String(), 0, val.Type().Bits()) @@ -162,14 +194,45 @@ func (d *Decoder) decodeUint(name string, data any, val reflect.Value) (err erro return err } +func (d *Decoder) decodeFloat(name string, data any, val reflect.Value) (err error) { + dataVal := reflect.ValueOf(data) + kind := dataVal.Kind() + switch { + case isFloat(kind): + val.SetFloat(dataVal.Float()) + case isUint(kind): + val.SetFloat(float64(dataVal.Uint())) + case isInt(kind) && d.option.WeaklyTypedInput: + val.SetFloat(float64(dataVal.Int())) + case kind == reflect.String && d.option.WeaklyTypedInput: + var i float64 + i, err = strconv.ParseFloat(dataVal.String(), val.Type().Bits()) + if err == nil { + val.SetFloat(i) + } else { + err = fmt.Errorf("cannot parse '%s' as int: %s", name, err) + } + default: + err = fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%s'", + name, val.Type(), dataVal.Type(), + ) + } + return err +} + func (d *Decoder) decodeString(name string, data any, val reflect.Value) (err error) { dataVal := reflect.ValueOf(data) kind := dataVal.Kind() switch { case kind == reflect.String: val.SetString(dataVal.String()) - case kind == reflect.Int && d.option.WeaklyTypedInput: + case isInt(kind) && d.option.WeaklyTypedInput: val.SetString(strconv.FormatInt(dataVal.Int(), 10)) + case isUint(kind) && d.option.WeaklyTypedInput: + val.SetString(strconv.FormatUint(dataVal.Uint(), 10)) + case isFloat(kind) && d.option.WeaklyTypedInput: + val.SetString(strconv.FormatFloat(dataVal.Float(), 'E', -1, dataVal.Type().Bits())) default: err = fmt.Errorf( "'%s' expected type '%s', got unconvertible type '%s'", @@ -185,8 +248,10 @@ func (d *Decoder) decodeBool(name string, data any, val reflect.Value) (err erro switch { case kind == reflect.Bool: val.SetBool(dataVal.Bool()) - case kind == reflect.Int && d.option.WeaklyTypedInput: + case isInt(kind) && d.option.WeaklyTypedInput: val.SetBool(dataVal.Int() != 0) + case isUint(kind) && d.option.WeaklyTypedInput: + val.SetString(strconv.FormatUint(dataVal.Uint(), 10)) default: err = fmt.Errorf( "'%s' expected type '%s', got unconvertible type '%s'", @@ -201,7 +266,7 @@ func (d *Decoder) decodeSlice(name string, data any, val reflect.Value) error { valType := val.Type() valElemType := valType.Elem() - if dataVal.Kind() == reflect.String && valElemType.Kind() == reflect.Uint8 { + if dataVal.Kind() == reflect.String && valElemType.Kind() == reflect.Uint8 { // from encoding/json s := []byte(dataVal.String()) b := make([]byte, base64.StdEncoding.DecodedLen(len(s))) n, err := base64.StdEncoding.Decode(b, s) From 287ec7e851a9d1f65284cbc1e61f81ff508c5e72 Mon Sep 17 00:00:00 2001 From: oluceps Date: Thu, 15 Dec 2022 13:25:18 +0800 Subject: [PATCH 233/250] chore: update flake lock --- flake.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flake.lock b/flake.lock index 7da95ac3..eba25bf6 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1664705630, - "narHash": "sha256-MLi1J9tIZQFj8v9RKmG89HJAE5ja3z4ui4Tf9+wG/bM=", + "lastModified": 1671072901, + "narHash": "sha256-eyFdLtfxYyZnbJorRiZ2kP2kW4gEU76hLzpZGW9mcZg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "f71b215225dec75df6266ff7764d54c2e44ef226", + "rev": "69ce4fbad877f91d4b9bc4cfedfb0ff1fe5043d5", "type": "github" }, "original": { @@ -24,11 +24,11 @@ }, "utils": { "locked": { - "lastModified": 1659877975, - "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "lastModified": 1667395993, + "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", "owner": "numtide", "repo": "flake-utils", - "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", "type": "github" }, "original": { From 98b73776433f2acb6cec870fcb4c9513b2beb424 Mon Sep 17 00:00:00 2001 From: oluceps Date: Thu, 15 Dec 2022 13:25:56 +0800 Subject: [PATCH 234/250] fix: spec Go 119 for nix build --- flake.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flake.nix b/flake.nix index 2b775e2f..c372ff4b 100644 --- a/flake.nix +++ b/flake.nix @@ -23,12 +23,12 @@ { overlay = final: prev: { - clash-meta = final.buildGoModule { + clash-meta = final.buildGo119Module { pname = "clash-meta"; inherit version; src = ./.; - vendorSha256 = "sha256-yhq4WHQcS4CrdcO6KJ5tSn4m7l5g1lNgE9/2BWd9Iys="; + vendorSha256 = "sha256-XVz2vts4on42lfxnov4jnUrHzSFF05+i1TVY3C7bgdw="; # Do not build testing suit excludedPackages = [ "./test" ]; From 57592ee840fd2a3b1b9e4086bb7180fc8380f2be Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 16 Dec 2022 22:15:44 +0800 Subject: [PATCH 235/250] chore: better safeConnClose --- adapter/outbound/http.go | 4 ++-- adapter/outbound/shadowsocks.go | 4 ++-- adapter/outbound/shadowsocksr.go | 4 ++-- adapter/outbound/snell.go | 4 ++-- adapter/outbound/socks5.go | 8 ++++---- adapter/outbound/trojan.go | 4 ++-- adapter/outbound/vless.go | 4 ++-- adapter/outbound/vmess.go | 4 ++-- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/adapter/outbound/http.go b/adapter/outbound/http.go index bdcf5c9f..c5c06208 100644 --- a/adapter/outbound/http.go +++ b/adapter/outbound/http.go @@ -67,9 +67,9 @@ func (h *Http) DialContext(ctx context.Context, metadata *C.Metadata, opts ...di } tcpKeepAlive(c) - defer func() { + defer func(c net.Conn) { safeConnClose(c, err) - }() + }(c) c, err = h.StreamConn(c, metadata) if err != nil { diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 263284b0..99f402cb 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -90,9 +90,9 @@ func (ss *ShadowSocks) DialContext(ctx context.Context, metadata *C.Metadata, op } tcpKeepAlive(c) - defer func() { + defer func(c net.Conn) { safeConnClose(c, err) - }() + }(c) c, err = ss.StreamConn(c, metadata) return NewConn(c, ss), err diff --git a/adapter/outbound/shadowsocksr.go b/adapter/outbound/shadowsocksr.go index c4112e6e..93c16401 100644 --- a/adapter/outbound/shadowsocksr.go +++ b/adapter/outbound/shadowsocksr.go @@ -66,9 +66,9 @@ func (ssr *ShadowSocksR) DialContext(ctx context.Context, metadata *C.Metadata, } tcpKeepAlive(c) - defer func() { + defer func(c net.Conn) { safeConnClose(c, err) - }() + }(c) c, err = ssr.StreamConn(c, metadata) return NewConn(c, ssr), err diff --git a/adapter/outbound/snell.go b/adapter/outbound/snell.go index f2bc6116..c4fe77ff 100644 --- a/adapter/outbound/snell.go +++ b/adapter/outbound/snell.go @@ -84,9 +84,9 @@ func (s *Snell) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d } tcpKeepAlive(c) - defer func() { + defer func(c net.Conn) { safeConnClose(c, err) - }() + }(c) c, err = s.StreamConn(c, metadata) return NewConn(c, s), err diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go index fe3b9bdc..62de1621 100644 --- a/adapter/outbound/socks5.go +++ b/adapter/outbound/socks5.go @@ -71,9 +71,9 @@ func (ss *Socks5) DialContext(ctx context.Context, metadata *C.Metadata, opts .. } tcpKeepAlive(c) - defer func() { + defer func(c net.Conn) { safeConnClose(c, err) - }() + }(c) c, err = ss.StreamConn(c, metadata) if err != nil { @@ -99,9 +99,9 @@ func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata, c = cc } - defer func() { + defer func(c net.Conn) { safeConnClose(c, err) - }() + }(c) tcpKeepAlive(c) var user *socks5.User diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index 9d3dcd4c..18d61c4a 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -127,9 +127,9 @@ func (t *Trojan) DialContext(ctx context.Context, metadata *C.Metadata, opts ... } tcpKeepAlive(c) - defer func() { + defer func(c net.Conn) { safeConnClose(c, err) - }() + }(c) c, err = t.StreamConn(c, metadata) if err != nil { diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index f1e62829..aaf60cb9 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -225,9 +225,9 @@ func (v *Vless) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } tcpKeepAlive(c) - defer func() { + defer func(c net.Conn) { safeConnClose(c, err) - }() + }(c) c, err = v.StreamConn(c, metadata) return NewConn(c, v), err diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index 65d3073b..9573cd08 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -238,9 +238,9 @@ func (v *Vmess) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } tcpKeepAlive(c) - defer func() { + defer func(c net.Conn) { safeConnClose(c, err) - }() + }(c) c, err = v.StreamConn(c, metadata) return NewConn(c, v), err From ff01d845b4722184ea6edf1cadbd74ecf2cc0112 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sat, 17 Dec 2022 11:03:37 +0800 Subject: [PATCH 236/250] fix: try to fix gvisor panic --- go.mod | 2 ++ go.sum | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index dad90614..3eb1fd91 100644 --- a/go.mod +++ b/go.mod @@ -74,3 +74,5 @@ require ( golang.org/x/tools v0.1.12 // indirect gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c // indirect ) + +replace gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c => github.com/metacubex/gvisor v0.0.0-20221217030112-bdcd835fd60e diff --git a/go.sum b/go.sum index 119e36f8..cc305000 100644 --- a/go.sum +++ b/go.sum @@ -90,6 +90,8 @@ github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZ github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg= github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw= github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc= +github.com/metacubex/gvisor v0.0.0-20221217030112-bdcd835fd60e h1:3PHqNvIAwYbv9cOQbRFIUgzJ+K6fhV1HHj+Vpg8U7g8= +github.com/metacubex/gvisor v0.0.0-20221217030112-bdcd835fd60e/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM= github.com/metacubex/quic-go v0.31.1-0.20221127023445-9f0ce65a734e h1:RnfC6+sShJ3biU2Q2wuh4FxZ8/3fp1QG+1zAfswVehA= github.com/metacubex/quic-go v0.31.1-0.20221127023445-9f0ce65a734e/go.mod h1:7NPWVTLiX2Ss9q9gBNZaNHsPqZ3Tg/ApyrXxxUYbl78= github.com/metacubex/sing-shadowsocks v0.1.0 h1:uGBtNkpy4QFlofaNkJf+iFegeLU11VzTUlkC46FHF8A= @@ -240,7 +242,5 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c h1:m5lcgWnL3OElQNVyp3qcncItJ2c0sQlSGjYK2+nJTA4= -gvisor.dev/gvisor v0.0.0-20220901235040-6ca97ef2ce1c/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM= lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= From c63dd62ed2a156bff72b19fd9526f3a56d6997e0 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 19 Dec 2022 17:02:04 +0800 Subject: [PATCH 237/250] chore: support relay native udp when using ss and ssr protocol --- adapter/outbound/base.go | 10 +++ adapter/outbound/shadowsocks.go | 16 +++++ adapter/outbound/shadowsocksr.go | 17 +++++ adapter/outboundgroup/relay.go | 119 +++++++++++++++++++++++++------ constant/adapters.go | 3 + 5 files changed, 144 insertions(+), 21 deletions(-) diff --git a/adapter/outbound/base.go b/adapter/outbound/base.go index a7e068dd..d328d5c6 100644 --- a/adapter/outbound/base.go +++ b/adapter/outbound/base.go @@ -62,6 +62,16 @@ func (b *Base) ListenPacketContext(ctx context.Context, metadata *C.Metadata, op return nil, errors.New("no support") } +// ListenPacketOnPacketConn implements C.ProxyAdapter +func (b *Base) ListenPacketOnPacketConn(ctx context.Context, c C.PacketConn, metadata *C.Metadata) (C.PacketConn, error) { + return nil, errors.New("no support") +} + +// SupportLPPC implements C.ProxyAdapter +func (b *Base) SupportLPPC() bool { + return false +} + // ListenPacketOnStreamConn implements C.ProxyAdapter func (b *Base) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { return nil, errors.New("no support") diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 99f402cb..4760e806 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -120,6 +120,22 @@ func (ss *ShadowSocks) ListenPacketContext(ctx context.Context, metadata *C.Meta return newPacketConn(pc, ss), nil } +// ListenPacketOnPacketConn implements C.ProxyAdapter +func (ss *ShadowSocks) ListenPacketOnPacketConn(ctx context.Context, c C.PacketConn, metadata *C.Metadata) (_ C.PacketConn, err error) { + addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ss.addr, ss.prefer) + if err != nil { + return nil, err + } + + pc := ss.method.DialPacketConn(&bufio.BindPacketConn{PacketConn: c, Addr: addr}) + return newPacketConn(pc, ss), nil +} + +// SupportLPPC implements C.ProxyAdapter +func (ss *ShadowSocks) SupportLPPC() bool { + return true +} + // ListenPacketOnStreamConn implements C.ProxyAdapter func (ss *ShadowSocks) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { if ss.option.UDPOverTCP { diff --git a/adapter/outbound/shadowsocksr.go b/adapter/outbound/shadowsocksr.go index 93c16401..237b9c03 100644 --- a/adapter/outbound/shadowsocksr.go +++ b/adapter/outbound/shadowsocksr.go @@ -91,6 +91,23 @@ func (ssr *ShadowSocksR) ListenPacketContext(ctx context.Context, metadata *C.Me return newPacketConn(&ssPacketConn{PacketConn: pc, rAddr: addr}, ssr), nil } +// ListenPacketOnPacketConn implements C.ProxyAdapter +func (ssr *ShadowSocksR) ListenPacketOnPacketConn(ctx context.Context, c C.PacketConn, metadata *C.Metadata) (_ C.PacketConn, err error) { + addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ssr.addr, ssr.prefer) + if err != nil { + return nil, err + } + + pc := ssr.cipher.PacketConn(c) + pc = ssr.protocol.PacketConn(pc) + return newPacketConn(&ssPacketConn{PacketConn: pc, rAddr: addr}, ssr), nil +} + +// SupportLPPC implements C.ProxyAdapter +func (ssr *ShadowSocksR) SupportLPPC() bool { + return true +} + func NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) { // SSR protocol compatibility // https://github.com/Dreamacro/clash/pull/2056 diff --git a/adapter/outboundgroup/relay.go b/adapter/outboundgroup/relay.go index 66954375..ee302097 100644 --- a/adapter/outboundgroup/relay.go +++ b/adapter/outboundgroup/relay.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "net" "github.com/Dreamacro/clash/adapter/outbound" "github.com/Dreamacro/clash/component/dialer" @@ -29,14 +30,23 @@ func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d first := proxies[0] last := proxies[len(proxies)-1] - c, err := dialer.DialContext(ctx, "tcp", first.Addr(), r.Base.DialOptions(opts...)...) + var c net.Conn + var currentMeta *C.Metadata + var err error + + currentMeta, err = addrToMetadata(proxies[1].Addr()) + if err != nil { + return nil, err + } + + c, err = first.DialContext(ctx, currentMeta, r.Base.DialOptions(opts...)...) if err != nil { return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) } - tcpKeepAlive(c) - var currentMeta *C.Metadata - for _, proxy := range proxies[1:] { + first = proxies[1] + + for _, proxy := range proxies[2:] { currentMeta, err = addrToMetadata(proxy.Addr()) if err != nil { return nil, err @@ -77,23 +87,85 @@ func (r *Relay) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o return proxies[0].ListenPacketContext(ctx, metadata, r.Base.DialOptions(opts...)...) } + udtId := -1 + for i, proxy := range proxies { + if !proxy.SupportUDP() { + return nil, fmt.Errorf("%s don't support udp", proxy.Name()) + } + if proxy.SupportUOT() { + udtId = i // we need the latest id, so don't break + } + } + first := proxies[0] last := proxies[len(proxies)-1] - c, err := dialer.DialContext(ctx, "tcp", first.Addr(), r.Base.DialOptions(opts...)...) - if err != nil { - return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) - } - tcpKeepAlive(c) - + var pc C.PacketConn var currentMeta *C.Metadata - for _, proxy := range proxies[1:] { + if udtId != -1 { + c, err := dialer.DialContext(ctx, "tcp", first.Addr(), r.Base.DialOptions(opts...)...) + if err != nil { + return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) + } + tcpKeepAlive(c) + + for _, proxy := range proxies[1 : udtId+1] { + currentMeta, err = addrToMetadata(proxy.Addr()) + if err != nil { + return nil, err + } + + c, err = first.StreamConn(c, currentMeta) + if err != nil { + return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) + } + + first = proxy + } + + if first == last { + currentMeta = metadata + } else { + currentMeta, err = addrToMetadata(proxies[udtId+1].Addr()) + if err != nil { + return nil, err + } + currentMeta.NetWork = C.UDP + } + c, err = first.StreamConn(c, currentMeta) + if err != nil { + return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) + } + + pc, err = first.ListenPacketOnStreamConn(c, currentMeta) + if err != nil { + return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) + } + if first == last { + return pc, nil + } + } else { + currentMeta, err = addrToMetadata(proxies[1].Addr()) + if err != nil { + return nil, err + } + currentMeta.NetWork = C.UDP + pc, err = first.ListenPacketContext(ctx, currentMeta, r.Base.DialOptions(opts...)...) + if err != nil { + return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) + } + udtId = 0 + } + + first = proxies[udtId+1] + for _, proxy := range proxies[udtId+2:] { currentMeta, err = addrToMetadata(proxy.Addr()) if err != nil { return nil, err } + currentMeta.NetWork = C.UDP - c, err = first.StreamConn(c, currentMeta) + pc, err = first.ListenPacketOnPacketConn(ctx, pc, currentMeta) if err != nil { return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) } @@ -101,17 +173,11 @@ func (r *Relay) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o first = proxy } - c, err = last.StreamConn(c, metadata) + pc, err = last.ListenPacketOnPacketConn(ctx, pc, metadata) if err != nil { return nil, fmt.Errorf("%s connect error: %w", last.Addr(), err) } - var pc C.PacketConn - pc, err = last.ListenPacketOnStreamConn(c, metadata) - if err != nil { - return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) - } - for i := len(chainProxies) - 2; i >= 0; i-- { pc.AppendToChains(chainProxies[i]) } @@ -127,8 +193,19 @@ func (r *Relay) SupportUDP() bool { if len(proxies) == 0 { // C.Direct return true } - last := proxies[len(proxies)-1] - return last.SupportUDP() && last.SupportUOT() + for i := len(proxies) - 1; i >= 0; i-- { + proxy := proxies[i] + if !proxy.SupportUDP() { + return false + } + if proxy.SupportUOT() { + return true + } + if !proxy.SupportLPPC() { + return false + } + } + return true } // MarshalJSON implements C.ProxyAdapter diff --git a/constant/adapters.go b/constant/adapters.go index f4b8ba4d..419708cf 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -108,6 +108,9 @@ type ProxyAdapter interface { SupportUOT() bool ListenPacketOnStreamConn(c net.Conn, metadata *Metadata) (PacketConn, error) + SupportLPPC() bool + ListenPacketOnPacketConn(ctx context.Context, c PacketConn, metadata *Metadata) (PacketConn, error) + // Unwrap extracts the proxy from a proxy-group. It returns nil when nothing to extract. Unwrap(metadata *Metadata, touch bool) Proxy } From 7496d9c114f712a1ea8f9643212078587bdd143b Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Mon, 19 Dec 2022 21:34:07 +0800 Subject: [PATCH 238/250] chore: rebuild relay --- adapter/outbound/base.go | 18 ++-- adapter/outbound/http.go | 12 ++- adapter/outbound/shadowsocks.go | 31 +++--- adapter/outbound/shadowsocksr.go | 30 +++--- adapter/outbound/snell.go | 21 ++-- adapter/outbound/socks5.go | 12 ++- adapter/outbound/trojan.go | 44 +++++--- adapter/outbound/vless.go | 49 +++++++-- adapter/outbound/vmess.go | 56 +++++++--- adapter/outboundgroup/relay.go | 174 +++++++++---------------------- adapter/outboundgroup/util.go | 2 +- common/net/bind.go | 36 +++++++ component/dialer/dialer.go | 12 +++ constant/adapters.go | 13 ++- dns/util.go | 32 +----- 15 files changed, 296 insertions(+), 246 deletions(-) create mode 100644 common/net/bind.go diff --git a/adapter/outbound/base.go b/adapter/outbound/base.go index d328d5c6..d7ffec5a 100644 --- a/adapter/outbound/base.go +++ b/adapter/outbound/base.go @@ -57,26 +57,26 @@ func (b *Base) DialContext(ctx context.Context, metadata *C.Metadata, opts ...di return nil, errors.New("no support") } +// DialContextWithDialer implements C.ProxyAdapter +func (b *Base) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { + return nil, errors.New("no support") +} + // ListenPacketContext implements C.ProxyAdapter func (b *Base) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { return nil, errors.New("no support") } -// ListenPacketOnPacketConn implements C.ProxyAdapter -func (b *Base) ListenPacketOnPacketConn(ctx context.Context, c C.PacketConn, metadata *C.Metadata) (C.PacketConn, error) { +// ListenPacketWithDialer implements C.ProxyAdapter +func (b *Base) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) { return nil, errors.New("no support") } -// SupportLPPC implements C.ProxyAdapter -func (b *Base) SupportLPPC() bool { +// SupportWithDialer implements C.ProxyAdapter +func (b *Base) SupportWithDialer() bool { return false } -// ListenPacketOnStreamConn implements C.ProxyAdapter -func (b *Base) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { - return nil, errors.New("no support") -} - // SupportUOT implements C.ProxyAdapter func (b *Base) SupportUOT() bool { return false diff --git a/adapter/outbound/http.go b/adapter/outbound/http.go index c5c06208..3e7060e6 100644 --- a/adapter/outbound/http.go +++ b/adapter/outbound/http.go @@ -61,7 +61,12 @@ func (h *Http) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { // DialContext implements C.ProxyAdapter func (h *Http) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { - c, err := dialer.DialContext(ctx, "tcp", h.addr, h.Base.DialOptions(opts...)...) + return h.DialContextWithDialer(ctx, dialer.Dialer{Options: h.Base.DialOptions(opts...)}, metadata) +} + +// DialContextWithDialer implements C.ProxyAdapter +func (h *Http) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { + c, err := dialer.DialContext(ctx, "tcp", h.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %w", h.addr, err) } @@ -79,6 +84,11 @@ func (h *Http) DialContext(ctx context.Context, metadata *C.Metadata, opts ...di return NewConn(c, h), nil } +// SupportWithDialer implements C.ProxyAdapter +func (h *Http) SupportWithDialer() bool { + return true +} + func (h *Http) shakeHand(metadata *C.Metadata, rw io.ReadWriter) error { addr := metadata.RemoteAddress() req := &http.Request{ diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 4760e806..46a8b9bf 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -84,7 +84,12 @@ func (ss *ShadowSocks) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, e // DialContext implements C.ProxyAdapter func (ss *ShadowSocks) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { - c, err := dialer.DialContext(ctx, "tcp", ss.addr, ss.Base.DialOptions(opts...)...) + return ss.DialContextWithDialer(ctx, dialer.Dialer{Options: ss.Base.DialOptions(opts...)}, metadata) +} + +// DialContextWithDialer implements C.ProxyAdapter +func (ss *ShadowSocks) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { + c, err := dialer.DialContext(ctx, "tcp", ss.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %w", ss.addr, err) } @@ -100,8 +105,13 @@ func (ss *ShadowSocks) DialContext(ctx context.Context, metadata *C.Metadata, op // ListenPacketContext implements C.ProxyAdapter func (ss *ShadowSocks) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { + return ss.ListenPacketWithDialer(ctx, dialer.Dialer{Options: ss.Base.DialOptions(opts...)}, metadata) +} + +// ListenPacketWithDialer implements C.ProxyAdapter +func (ss *ShadowSocks) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) { if ss.option.UDPOverTCP { - tcpConn, err := ss.DialContext(ctx, metadata, opts...) + tcpConn, err := ss.DialContextWithDialer(ctx, dialer, metadata) if err != nil { return nil, err } @@ -112,7 +122,7 @@ func (ss *ShadowSocks) ListenPacketContext(ctx context.Context, metadata *C.Meta return nil, err } - pc, err := dialer.ListenPacket(ctx, dialer.ParseNetwork("udp", addr.AddrPort().Addr()), "", ss.Base.DialOptions(opts...)...) + pc, err := dialer.ListenPacket(ctx, "udp", "", addr.AddrPort()) if err != nil { return nil, err } @@ -120,19 +130,8 @@ func (ss *ShadowSocks) ListenPacketContext(ctx context.Context, metadata *C.Meta return newPacketConn(pc, ss), nil } -// ListenPacketOnPacketConn implements C.ProxyAdapter -func (ss *ShadowSocks) ListenPacketOnPacketConn(ctx context.Context, c C.PacketConn, metadata *C.Metadata) (_ C.PacketConn, err error) { - addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ss.addr, ss.prefer) - if err != nil { - return nil, err - } - - pc := ss.method.DialPacketConn(&bufio.BindPacketConn{PacketConn: c, Addr: addr}) - return newPacketConn(pc, ss), nil -} - -// SupportLPPC implements C.ProxyAdapter -func (ss *ShadowSocks) SupportLPPC() bool { +// SupportWithDialer implements C.ProxyAdapter +func (ss *ShadowSocks) SupportWithDialer() bool { return true } diff --git a/adapter/outbound/shadowsocksr.go b/adapter/outbound/shadowsocksr.go index 237b9c03..99b8edc3 100644 --- a/adapter/outbound/shadowsocksr.go +++ b/adapter/outbound/shadowsocksr.go @@ -60,7 +60,12 @@ func (ssr *ShadowSocksR) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, // DialContext implements C.ProxyAdapter func (ssr *ShadowSocksR) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { - c, err := dialer.DialContext(ctx, "tcp", ssr.addr, ssr.Base.DialOptions(opts...)...) + return ssr.DialContextWithDialer(ctx, dialer.Dialer{Options: ssr.Base.DialOptions(opts...)}, metadata) +} + +// DialContextWithDialer implements C.ProxyAdapter +func (ssr *ShadowSocksR) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { + c, err := dialer.DialContext(ctx, "tcp", ssr.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %w", ssr.addr, err) } @@ -76,12 +81,17 @@ func (ssr *ShadowSocksR) DialContext(ctx context.Context, metadata *C.Metadata, // ListenPacketContext implements C.ProxyAdapter func (ssr *ShadowSocksR) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { + return ssr.ListenPacketWithDialer(ctx, dialer.Dialer{Options: ssr.Base.DialOptions(opts...)}, metadata) +} + +// ListenPacketWithDialer implements C.ProxyAdapter +func (ssr *ShadowSocksR) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) { addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ssr.addr, ssr.prefer) if err != nil { return nil, err } - pc, err := dialer.ListenPacket(ctx, dialer.ParseNetwork("udp", addr.AddrPort().Addr()), "", ssr.Base.DialOptions(opts...)...) + pc, err := dialer.ListenPacket(ctx, "udp", "", addr.AddrPort()) if err != nil { return nil, err } @@ -91,20 +101,8 @@ func (ssr *ShadowSocksR) ListenPacketContext(ctx context.Context, metadata *C.Me return newPacketConn(&ssPacketConn{PacketConn: pc, rAddr: addr}, ssr), nil } -// ListenPacketOnPacketConn implements C.ProxyAdapter -func (ssr *ShadowSocksR) ListenPacketOnPacketConn(ctx context.Context, c C.PacketConn, metadata *C.Metadata) (_ C.PacketConn, err error) { - addr, err := resolveUDPAddrWithPrefer(ctx, "udp", ssr.addr, ssr.prefer) - if err != nil { - return nil, err - } - - pc := ssr.cipher.PacketConn(c) - pc = ssr.protocol.PacketConn(pc) - return newPacketConn(&ssPacketConn{PacketConn: pc, rAddr: addr}, ssr), nil -} - -// SupportLPPC implements C.ProxyAdapter -func (ssr *ShadowSocksR) SupportLPPC() bool { +// SupportWithDialer implements C.ProxyAdapter +func (ssr *ShadowSocksR) SupportWithDialer() bool { return true } diff --git a/adapter/outbound/snell.go b/adapter/outbound/snell.go index c4fe77ff..bc1fa0c1 100644 --- a/adapter/outbound/snell.go +++ b/adapter/outbound/snell.go @@ -78,7 +78,12 @@ func (s *Snell) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d return NewConn(c, s), err } - c, err := dialer.DialContext(ctx, "tcp", s.addr, s.Base.DialOptions(opts...)...) + return s.DialContextWithDialer(ctx, dialer.Dialer{Options: s.Base.DialOptions(opts...)}, metadata) +} + +// DialContextWithDialer implements C.ProxyAdapter +func (s *Snell) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { + c, err := dialer.DialContext(ctx, "tcp", s.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %w", s.addr, err) } @@ -94,7 +99,12 @@ func (s *Snell) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d // ListenPacketContext implements C.ProxyAdapter func (s *Snell) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { - c, err := dialer.DialContext(ctx, "tcp", s.addr, s.Base.DialOptions(opts...)...) + return s.ListenPacketWithDialer(ctx, dialer.Dialer{Options: s.Base.DialOptions(opts...)}, metadata) +} + +// ListenPacketWithDialer implements C.ProxyAdapter +func (s *Snell) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (C.PacketConn, error) { + c, err := dialer.DialContext(ctx, "tcp", s.addr) if err != nil { return nil, err } @@ -110,10 +120,9 @@ func (s *Snell) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o return newPacketConn(pc, s), nil } -// ListenPacketOnStreamConn implements C.ProxyAdapter -func (s *Snell) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { - pc := snell.PacketConn(c) - return newPacketConn(pc, s), nil +// SupportWithDialer implements C.ProxyAdapter +func (s *Snell) SupportWithDialer() bool { + return true } // SupportUOT implements C.ProxyAdapter diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go index 62de1621..ccd13da7 100644 --- a/adapter/outbound/socks5.go +++ b/adapter/outbound/socks5.go @@ -65,7 +65,12 @@ func (ss *Socks5) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) // DialContext implements C.ProxyAdapter func (ss *Socks5) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { - c, err := dialer.DialContext(ctx, "tcp", ss.addr, ss.Base.DialOptions(opts...)...) + return ss.DialContextWithDialer(ctx, dialer.Dialer{Options: ss.Base.DialOptions(opts...)}, metadata) +} + +// DialContextWithDialer implements C.ProxyAdapter +func (ss *Socks5) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { + c, err := dialer.DialContext(ctx, "tcp", ss.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %w", ss.addr, err) } @@ -83,6 +88,11 @@ func (ss *Socks5) DialContext(ctx context.Context, metadata *C.Metadata, opts .. return NewConn(c, ss), nil } +// SupportWithDialer implements C.ProxyAdapter +func (ss *Socks5) SupportWithDialer() bool { + return true +} + // ListenPacketContext implements C.ProxyAdapter func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { c, err := dialer.DialContext(ctx, "tcp", ss.addr, ss.Base.DialOptions(opts...)...) diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index 18d61c4a..9bfb0126 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -120,8 +120,12 @@ func (t *Trojan) DialContext(ctx context.Context, metadata *C.Metadata, opts ... return NewConn(c, t), nil } + return t.DialContextWithDialer(ctx, dialer.Dialer{Options: t.Base.DialOptions(opts...)}, metadata) +} - c, err := dialer.DialContext(ctx, "tcp", t.addr, t.Base.DialOptions(opts...)...) +// DialContextWithDialer implements C.ProxyAdapter +func (t *Trojan) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { + c, err := dialer.DialContext(ctx, "tcp", t.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %w", t.addr, err) } @@ -152,19 +156,30 @@ func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata, defer func() { safeConnClose(c, err) }() - } else { - c, err = dialer.DialContext(ctx, "tcp", t.addr, t.Base.DialOptions(opts...)...) + err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata)) if err != nil { - return nil, fmt.Errorf("%s connect error: %w", t.addr, err) - } - defer func() { - safeConnClose(c, err) - }() - tcpKeepAlive(c) - c, err = t.plainStream(c) - if err != nil { - return nil, fmt.Errorf("%s connect error: %w", t.addr, err) + return nil, err } + + pc := t.instance.PacketConn(c) + return newPacketConn(pc, t), err + } + return t.ListenPacketWithDialer(ctx, dialer.Dialer{Options: t.Base.DialOptions(opts...)}, metadata) +} + +// ListenPacketWithDialer implements C.ProxyAdapter +func (t *Trojan) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) { + c, err := dialer.DialContext(ctx, "tcp", t.addr) + if err != nil { + return nil, fmt.Errorf("%s connect error: %w", t.addr, err) + } + defer func() { + safeConnClose(c, err) + }() + tcpKeepAlive(c) + c, err = t.plainStream(c) + if err != nil { + return nil, fmt.Errorf("%s connect error: %w", t.addr, err) } err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata)) @@ -176,6 +191,11 @@ func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata, return newPacketConn(pc, t), err } +// SupportWithDialer implements C.ProxyAdapter +func (t *Trojan) SupportWithDialer() bool { + return true +} + // ListenPacketOnStreamConn implements C.ProxyAdapter func (t *Trojan) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { pc := t.instance.PacketConn(c) diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index aaf60cb9..28ebfb05 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -219,8 +219,12 @@ func (v *Vless) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d return NewConn(c, v), nil } + return v.DialContextWithDialer(ctx, dialer.Dialer{Options: v.Base.DialOptions(opts...)}, metadata) +} - c, err := dialer.DialContext(ctx, "tcp", v.addr, v.Base.DialOptions(opts...)...) +// DialContextWithDialer implements C.ProxyAdapter +func (v *Vless) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { + c, err := dialer.DialContext(ctx, "tcp", v.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } @@ -256,18 +260,36 @@ func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o }() c, err = v.client.StreamConn(c, parseVlessAddr(metadata)) - } else { - c, err = dialer.DialContext(ctx, "tcp", v.addr, v.Base.DialOptions(opts...)...) - if err != nil { - return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) - } - tcpKeepAlive(c) - defer func() { - safeConnClose(c, err) - }() - c, err = v.StreamConn(c, metadata) + if err != nil { + return nil, fmt.Errorf("new vless client error: %v", err) + } + + return v.ListenPacketOnStreamConn(c, metadata) } + return v.ListenPacketWithDialer(ctx, dialer.Dialer{Options: v.Base.DialOptions(opts...)}, metadata) +} + +// ListenPacketWithDialer implements C.ProxyAdapter +func (v *Vless) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) { + // vless use stream-oriented udp with a special address, so we needs a net.UDPAddr + if !metadata.Resolved() { + ip, err := resolver.ResolveIP(ctx, metadata.Host) + if err != nil { + return nil, errors.New("can't resolve ip") + } + metadata.DstIP = ip + } + c, err := dialer.DialContext(ctx, "tcp", v.addr) + if err != nil { + return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) + } + tcpKeepAlive(c) + defer func() { + safeConnClose(c, err) + }() + + c, err = v.StreamConn(c, metadata) if err != nil { return nil, fmt.Errorf("new vless client error: %v", err) @@ -276,6 +298,11 @@ func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o return v.ListenPacketOnStreamConn(c, metadata) } +// SupportWithDialer implements C.ProxyAdapter +func (v *Vless) SupportWithDialer() bool { + return true +} + // ListenPacketOnStreamConn implements C.ProxyAdapter func (v *Vless) ListenPacketOnStreamConn(c net.Conn, metadata *C.Metadata) (_ C.PacketConn, err error) { return newPacketConn(&vlessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index 9573cd08..5697e056 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -232,8 +232,12 @@ func (v *Vmess) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d return NewConn(c, v), nil } + return v.DialContextWithDialer(ctx, dialer.Dialer{Options: v.Base.DialOptions(opts...)}, metadata) +} - c, err := dialer.DialContext(ctx, "tcp", v.addr, v.Base.DialOptions(opts...)...) +// DialContextWithDialer implements C.ProxyAdapter +func (v *Vmess) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.Conn, err error) { + c, err := dialer.DialContext(ctx, "tcp", v.addr) if err != nil { return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } @@ -280,29 +284,47 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o } else { c, err = v.client.DialPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) } - } else { - c, err = dialer.DialContext(ctx, "tcp", v.addr, v.Base.DialOptions(opts...)...) - if err != nil { - return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) - } - tcpKeepAlive(c) - defer func() { - safeConnClose(c, err) - }() - c, err = v.StreamConn(c, metadata) + if err != nil { + return nil, fmt.Errorf("new vmess client error: %v", err) + } + return v.ListenPacketOnStreamConn(c, metadata) + } + c, err = dialer.DialContext(ctx, "tcp", v.addr, v.Base.DialOptions(opts...)...) + if err != nil { + return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) + } + tcpKeepAlive(c) + defer func() { + safeConnClose(c, err) + }() + + c, err = v.StreamConn(c, metadata) + return v.ListenPacketWithDialer(ctx, dialer.Dialer{Options: v.Base.DialOptions(opts...)}, metadata) +} + +// ListenPacketWithDialer implements C.ProxyAdapter +func (v *Vmess) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) { + // vmess use stream-oriented udp with a special address, so we needs a net.UDPAddr + if !metadata.Resolved() { + ip, err := resolver.ResolveIP(ctx, metadata.Host) + if err != nil { + return nil, errors.New("can't resolve ip") + } + metadata.DstIP = ip } + c, err := dialer.DialContext(ctx, "tcp", v.addr) if err != nil { return nil, fmt.Errorf("new vmess client error: %v", err) } - if v.option.PacketAddr { - return newPacketConn(&threadSafePacketConn{PacketConn: packetaddr.NewBindConn(c)}, v), nil - } else if pc, ok := c.(net.PacketConn); ok { - return newPacketConn(&threadSafePacketConn{PacketConn: pc}, v), nil - } - return newPacketConn(&vmessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil + return v.ListenPacketOnStreamConn(c, metadata) +} + +// SupportWithDialer implements C.ProxyAdapter +func (v *Vmess) SupportWithDialer() bool { + return true } // ListenPacketOnStreamConn implements C.ProxyAdapter diff --git a/adapter/outboundgroup/relay.go b/adapter/outboundgroup/relay.go index ee302097..8205ee52 100644 --- a/adapter/outboundgroup/relay.go +++ b/adapter/outboundgroup/relay.go @@ -3,10 +3,12 @@ package outboundgroup import ( "context" "encoding/json" - "fmt" "net" + "net/netip" + "strings" "github.com/Dreamacro/clash/adapter/outbound" + N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/constant/provider" @@ -16,6 +18,36 @@ type Relay struct { *GroupBase } +type proxyDialer struct { + proxy C.Proxy + dialer C.Dialer +} + +func (p proxyDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { + currentMeta, err := addrToMetadata(address) + if err != nil { + return nil, err + } + if strings.Contains(network, "udp") { // should not support this operation + currentMeta.NetWork = C.UDP + pc, err := p.proxy.ListenPacketWithDialer(ctx, p.dialer, currentMeta) + if err != nil { + return nil, err + } + return N.NewBindPacketConn(pc, currentMeta.UDPAddr()), nil + } + return p.proxy.DialContextWithDialer(ctx, p.dialer, currentMeta) +} + +func (p proxyDialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) { + currentMeta, err := addrToMetadata(rAddrPort.String()) + if err != nil { + return nil, err + } + currentMeta.NetWork = C.UDP + return p.proxy.ListenPacketWithDialer(ctx, p.dialer, currentMeta) +} + // DialContext implements C.ProxyAdapter func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { proxies, chainProxies := r.proxies(metadata, true) @@ -26,47 +58,20 @@ func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d case 1: return proxies[0].DialContext(ctx, metadata, r.Base.DialOptions(opts...)...) } - - first := proxies[0] + var d C.Dialer + d = dialer.Dialer{Options: r.Base.DialOptions(opts...)} + for _, proxy := range proxies[:len(proxies)-1] { + d = proxyDialer{ + proxy: proxy, + dialer: d, + } + } last := proxies[len(proxies)-1] - - var c net.Conn - var currentMeta *C.Metadata - var err error - - currentMeta, err = addrToMetadata(proxies[1].Addr()) + conn, err := last.DialContextWithDialer(ctx, d, metadata) if err != nil { return nil, err } - c, err = first.DialContext(ctx, currentMeta, r.Base.DialOptions(opts...)...) - if err != nil { - return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) - } - - first = proxies[1] - - for _, proxy := range proxies[2:] { - currentMeta, err = addrToMetadata(proxy.Addr()) - if err != nil { - return nil, err - } - - c, err = first.StreamConn(c, currentMeta) - if err != nil { - return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) - } - - first = proxy - } - - c, err = last.StreamConn(c, metadata) - if err != nil { - return nil, fmt.Errorf("%s connect error: %w", last.Addr(), err) - } - - conn := outbound.NewConn(c, last) - for i := len(chainProxies) - 2; i >= 0; i-- { conn.AppendToChains(chainProxies[i]) } @@ -87,95 +92,18 @@ func (r *Relay) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o return proxies[0].ListenPacketContext(ctx, metadata, r.Base.DialOptions(opts...)...) } - udtId := -1 - for i, proxy := range proxies { - if !proxy.SupportUDP() { - return nil, fmt.Errorf("%s don't support udp", proxy.Name()) - } - if proxy.SupportUOT() { - udtId = i // we need the latest id, so don't break + var d C.Dialer + d = dialer.Dialer{Options: r.Base.DialOptions(opts...)} + for _, proxy := range proxies[:len(proxies)-1] { + d = proxyDialer{ + proxy: proxy, + dialer: d, } } - - first := proxies[0] last := proxies[len(proxies)-1] - - var pc C.PacketConn - var currentMeta *C.Metadata - if udtId != -1 { - c, err := dialer.DialContext(ctx, "tcp", first.Addr(), r.Base.DialOptions(opts...)...) - if err != nil { - return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) - } - tcpKeepAlive(c) - - for _, proxy := range proxies[1 : udtId+1] { - currentMeta, err = addrToMetadata(proxy.Addr()) - if err != nil { - return nil, err - } - - c, err = first.StreamConn(c, currentMeta) - if err != nil { - return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) - } - - first = proxy - } - - if first == last { - currentMeta = metadata - } else { - currentMeta, err = addrToMetadata(proxies[udtId+1].Addr()) - if err != nil { - return nil, err - } - currentMeta.NetWork = C.UDP - } - c, err = first.StreamConn(c, currentMeta) - if err != nil { - return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) - } - - pc, err = first.ListenPacketOnStreamConn(c, currentMeta) - if err != nil { - return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) - } - if first == last { - return pc, nil - } - } else { - currentMeta, err = addrToMetadata(proxies[1].Addr()) - if err != nil { - return nil, err - } - currentMeta.NetWork = C.UDP - pc, err = first.ListenPacketContext(ctx, currentMeta, r.Base.DialOptions(opts...)...) - if err != nil { - return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) - } - udtId = 0 - } - - first = proxies[udtId+1] - for _, proxy := range proxies[udtId+2:] { - currentMeta, err = addrToMetadata(proxy.Addr()) - if err != nil { - return nil, err - } - currentMeta.NetWork = C.UDP - - pc, err = first.ListenPacketOnPacketConn(ctx, pc, currentMeta) - if err != nil { - return nil, fmt.Errorf("%s connect error: %w", first.Addr(), err) - } - - first = proxy - } - - pc, err = last.ListenPacketOnPacketConn(ctx, pc, metadata) + pc, err := last.ListenPacketWithDialer(ctx, d, metadata) if err != nil { - return nil, fmt.Errorf("%s connect error: %w", last.Addr(), err) + return nil, err } for i := len(chainProxies) - 2; i >= 0; i-- { @@ -201,7 +129,7 @@ func (r *Relay) SupportUDP() bool { if proxy.SupportUOT() { return true } - if !proxy.SupportLPPC() { + if !proxy.SupportWithDialer() { return false } } diff --git a/adapter/outboundgroup/util.go b/adapter/outboundgroup/util.go index 3c769b2f..578011f8 100644 --- a/adapter/outboundgroup/util.go +++ b/adapter/outboundgroup/util.go @@ -24,7 +24,7 @@ func addrToMetadata(rawAddress string) (addr *C.Metadata, err error) { } else { addr = &C.Metadata{ Host: "", - DstIP: ip, + DstIP: ip.Unmap(), DstPort: port, } } diff --git a/common/net/bind.go b/common/net/bind.go new file mode 100644 index 00000000..1e20a8c0 --- /dev/null +++ b/common/net/bind.go @@ -0,0 +1,36 @@ +package net + +import "net" + +type bindPacketConn struct { + net.PacketConn + rAddr net.Addr +} + +func (wpc *bindPacketConn) Read(b []byte) (n int, err error) { + n, _, err = wpc.PacketConn.ReadFrom(b) + return n, err +} + +func (wpc *bindPacketConn) Write(b []byte) (n int, err error) { + return wpc.PacketConn.WriteTo(b, wpc.rAddr) +} + +func (wpc *bindPacketConn) RemoteAddr() net.Addr { + return wpc.rAddr +} + +func (wpc *bindPacketConn) LocalAddr() net.Addr { + if wpc.PacketConn.LocalAddr() == nil { + return &net.UDPAddr{IP: net.IPv4zero, Port: 0} + } else { + return wpc.PacketConn.LocalAddr() + } +} + +func NewBindPacketConn(pc net.PacketConn, rAddr net.Addr) net.Conn { + return &bindPacketConn{ + PacketConn: pc, + rAddr: rAddr, + } +} diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index c6b57d6c..256ff495 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -444,3 +444,15 @@ func concurrentIPv6DialContext(ctx context.Context, network, address string, opt return concurrentDialContext(ctx, network, ips, port, opt) } + +type Dialer struct { + Options []Option +} + +func (d Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { + return DialContext(ctx, network, address, d.Options...) +} + +func (d Dialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) { + return ListenPacket(ctx, ParseNetwork(network, rAddrPort.Addr()), address, d.Options...) +} diff --git a/constant/adapters.go b/constant/adapters.go index 419708cf..c13a85c4 100644 --- a/constant/adapters.go +++ b/constant/adapters.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "net" + "net/netip" "time" "github.com/Dreamacro/clash/component/dialer" @@ -81,6 +82,11 @@ type PacketConn interface { // WriteWithMetadata(p []byte, metadata *Metadata) (n int, err error) } +type Dialer interface { + DialContext(ctx context.Context, network, address string) (net.Conn, error) + ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) +} + type ProxyAdapter interface { Name() string Type() AdapterType @@ -89,6 +95,7 @@ type ProxyAdapter interface { SupportTFO() bool MarshalJSON() ([]byte, error) + // Deprecated: use DialContextWithDialer and ListenPacketWithDialer instead. // StreamConn wraps a protocol around net.Conn with Metadata. // // Examples: @@ -106,10 +113,10 @@ type ProxyAdapter interface { // SupportUOT return UDP over TCP support SupportUOT() bool - ListenPacketOnStreamConn(c net.Conn, metadata *Metadata) (PacketConn, error) - SupportLPPC() bool - ListenPacketOnPacketConn(ctx context.Context, c PacketConn, metadata *Metadata) (PacketConn, error) + SupportWithDialer() bool + DialContextWithDialer(ctx context.Context, dialer Dialer, metadata *Metadata) (Conn, error) + ListenPacketWithDialer(ctx context.Context, dialer Dialer, metadata *Metadata) (PacketConn, error) // Unwrap extracts the proxy from a proxy-group. It returns nil when nothing to extract. Unwrap(metadata *Metadata, touch bool) Proxy diff --git a/dns/util.go b/dns/util.go index dfd2bafd..3f7700ea 100644 --- a/dns/util.go +++ b/dns/util.go @@ -11,6 +11,7 @@ import ( "time" "github.com/Dreamacro/clash/common/cache" + N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/nnip" "github.com/Dreamacro/clash/common/picker" "github.com/Dreamacro/clash/component/dialer" @@ -134,32 +135,6 @@ func msgToDomain(msg *D.Msg) string { return "" } -type wrapPacketConn struct { - net.PacketConn - rAddr net.Addr -} - -func (wpc *wrapPacketConn) Read(b []byte) (n int, err error) { - n, _, err = wpc.PacketConn.ReadFrom(b) - return n, err -} - -func (wpc *wrapPacketConn) Write(b []byte) (n int, err error) { - return wpc.PacketConn.WriteTo(b, wpc.rAddr) -} - -func (wpc *wrapPacketConn) RemoteAddr() net.Addr { - return wpc.rAddr -} - -func (wpc *wrapPacketConn) LocalAddr() net.Addr { - if wpc.PacketConn.LocalAddr() == nil { - return &net.UDPAddr{IP: net.IPv4zero, Port: 0} - } else { - return wpc.PacketConn.LocalAddr() - } -} - type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error) func getDialHandler(r *Resolver, proxyAdapter string, opts ...dialer.Option) dialHandler { @@ -213,10 +188,7 @@ func getDialHandler(r *Resolver, proxyAdapter string, opts ...dialer.Option) dia return nil, err } - return &wrapPacketConn{ - PacketConn: packetConn, - rAddr: metadata.UDPAddr(), - }, nil + return N.NewBindPacketConn(packetConn, metadata.UDPAddr()), nil } } } From 22414ce399f98480c2f0afc92cebb596de6eb3ea Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Tue, 20 Dec 2022 00:11:02 +0800 Subject: [PATCH 239/250] chore: relay support tuic --- adapter/outbound/http.go | 2 +- adapter/outbound/shadowsocks.go | 4 +-- adapter/outbound/shadowsocksr.go | 4 +-- adapter/outbound/snell.go | 4 +-- adapter/outbound/socks5.go | 2 +- adapter/outbound/trojan.go | 4 +-- adapter/outbound/tuic.go | 29 +++++++++++++++++- adapter/outbound/vless.go | 4 +-- adapter/outbound/vmess.go | 4 +-- adapter/outboundgroup/relay.go | 4 +-- component/dialer/dialer.go | 17 +++++++---- component/dialer/options.go | 6 ++++ transport/tuic/client.go | 1 + transport/tuic/pool_client.go | 50 +++++++++++++++++++++++++------- 14 files changed, 101 insertions(+), 34 deletions(-) diff --git a/adapter/outbound/http.go b/adapter/outbound/http.go index 3e7060e6..b734290a 100644 --- a/adapter/outbound/http.go +++ b/adapter/outbound/http.go @@ -61,7 +61,7 @@ func (h *Http) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) { // DialContext implements C.ProxyAdapter func (h *Http) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { - return h.DialContextWithDialer(ctx, dialer.Dialer{Options: h.Base.DialOptions(opts...)}, metadata) + return h.DialContextWithDialer(ctx, dialer.NewDialer(h.Base.DialOptions(opts...)...), metadata) } // DialContextWithDialer implements C.ProxyAdapter diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 46a8b9bf..c318d263 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -84,7 +84,7 @@ func (ss *ShadowSocks) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, e // DialContext implements C.ProxyAdapter func (ss *ShadowSocks) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { - return ss.DialContextWithDialer(ctx, dialer.Dialer{Options: ss.Base.DialOptions(opts...)}, metadata) + return ss.DialContextWithDialer(ctx, dialer.NewDialer(ss.Base.DialOptions(opts...)...), metadata) } // DialContextWithDialer implements C.ProxyAdapter @@ -105,7 +105,7 @@ func (ss *ShadowSocks) DialContextWithDialer(ctx context.Context, dialer C.Diale // ListenPacketContext implements C.ProxyAdapter func (ss *ShadowSocks) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { - return ss.ListenPacketWithDialer(ctx, dialer.Dialer{Options: ss.Base.DialOptions(opts...)}, metadata) + return ss.ListenPacketWithDialer(ctx, dialer.NewDialer(ss.Base.DialOptions(opts...)...), metadata) } // ListenPacketWithDialer implements C.ProxyAdapter diff --git a/adapter/outbound/shadowsocksr.go b/adapter/outbound/shadowsocksr.go index 99b8edc3..e84de879 100644 --- a/adapter/outbound/shadowsocksr.go +++ b/adapter/outbound/shadowsocksr.go @@ -60,7 +60,7 @@ func (ssr *ShadowSocksR) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, // DialContext implements C.ProxyAdapter func (ssr *ShadowSocksR) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { - return ssr.DialContextWithDialer(ctx, dialer.Dialer{Options: ssr.Base.DialOptions(opts...)}, metadata) + return ssr.DialContextWithDialer(ctx, dialer.NewDialer(ssr.Base.DialOptions(opts...)...), metadata) } // DialContextWithDialer implements C.ProxyAdapter @@ -81,7 +81,7 @@ func (ssr *ShadowSocksR) DialContextWithDialer(ctx context.Context, dialer C.Dia // ListenPacketContext implements C.ProxyAdapter func (ssr *ShadowSocksR) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { - return ssr.ListenPacketWithDialer(ctx, dialer.Dialer{Options: ssr.Base.DialOptions(opts...)}, metadata) + return ssr.ListenPacketWithDialer(ctx, dialer.NewDialer(ssr.Base.DialOptions(opts...)...), metadata) } // ListenPacketWithDialer implements C.ProxyAdapter diff --git a/adapter/outbound/snell.go b/adapter/outbound/snell.go index bc1fa0c1..1331b526 100644 --- a/adapter/outbound/snell.go +++ b/adapter/outbound/snell.go @@ -78,7 +78,7 @@ func (s *Snell) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d return NewConn(c, s), err } - return s.DialContextWithDialer(ctx, dialer.Dialer{Options: s.Base.DialOptions(opts...)}, metadata) + return s.DialContextWithDialer(ctx, dialer.NewDialer(s.Base.DialOptions(opts...)...), metadata) } // DialContextWithDialer implements C.ProxyAdapter @@ -99,7 +99,7 @@ func (s *Snell) DialContextWithDialer(ctx context.Context, dialer C.Dialer, meta // ListenPacketContext implements C.ProxyAdapter func (s *Snell) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) { - return s.ListenPacketWithDialer(ctx, dialer.Dialer{Options: s.Base.DialOptions(opts...)}, metadata) + return s.ListenPacketWithDialer(ctx, dialer.NewDialer(s.Base.DialOptions(opts...)...), metadata) } // ListenPacketWithDialer implements C.ProxyAdapter diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go index ccd13da7..28d41180 100644 --- a/adapter/outbound/socks5.go +++ b/adapter/outbound/socks5.go @@ -65,7 +65,7 @@ func (ss *Socks5) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) // DialContext implements C.ProxyAdapter func (ss *Socks5) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.Conn, err error) { - return ss.DialContextWithDialer(ctx, dialer.Dialer{Options: ss.Base.DialOptions(opts...)}, metadata) + return ss.DialContextWithDialer(ctx, dialer.NewDialer(ss.Base.DialOptions(opts...)...), metadata) } // DialContextWithDialer implements C.ProxyAdapter diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index 9bfb0126..e3abeab2 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -120,7 +120,7 @@ func (t *Trojan) DialContext(ctx context.Context, metadata *C.Metadata, opts ... return NewConn(c, t), nil } - return t.DialContextWithDialer(ctx, dialer.Dialer{Options: t.Base.DialOptions(opts...)}, metadata) + return t.DialContextWithDialer(ctx, dialer.NewDialer(t.Base.DialOptions(opts...)...), metadata) } // DialContextWithDialer implements C.ProxyAdapter @@ -164,7 +164,7 @@ func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata, pc := t.instance.PacketConn(c) return newPacketConn(pc, t), err } - return t.ListenPacketWithDialer(ctx, dialer.Dialer{Options: t.Base.DialOptions(opts...)}, metadata) + return t.ListenPacketWithDialer(ctx, dialer.NewDialer(t.Base.DialOptions(opts...)...), metadata) } // ListenPacketWithDialer implements C.ProxyAdapter diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index fa171187..6ffc0095 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -63,6 +63,15 @@ func (t *Tuic) DialContext(ctx context.Context, metadata *C.Metadata, opts ...di return NewConn(conn, t), err } +// DialContextWithDialer implements C.ProxyAdapter +func (t *Tuic) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (C.Conn, error) { + conn, err := t.client.DialContextWithDialer(ctx, dialer, metadata, t.dialWithDialer) + if err != nil { + return nil, err + } + return NewConn(conn, t), err +} + // ListenPacketContext implements C.ProxyAdapter func (t *Tuic) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { opts = t.Base.DialOptions(opts...) @@ -73,13 +82,31 @@ func (t *Tuic) ListenPacketContext(ctx context.Context, metadata *C.Metadata, op return newPacketConn(pc, t), nil } +// ListenPacketWithDialer implements C.ProxyAdapter +func (t *Tuic) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) { + pc, err := t.client.ListenPacketWithDialer(ctx, dialer, metadata, t.dialWithDialer) + if err != nil { + return nil, err + } + return newPacketConn(pc, t), nil +} + +// SupportWithDialer implements C.ProxyAdapter +func (t *Tuic) SupportWithDialer() bool { + return true +} + func (t *Tuic) dial(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) { + return t.dialWithDialer(ctx, dialer.NewDialer(opts...)) +} + +func (t *Tuic) dialWithDialer(ctx context.Context, dialer C.Dialer) (pc net.PacketConn, addr net.Addr, err error) { udpAddr, err := resolveUDPAddrWithPrefer(ctx, "udp", t.addr, t.prefer) if err != nil { return nil, nil, err } addr = udpAddr - pc, err = dialer.ListenPacket(ctx, dialer.ParseNetwork("udp", udpAddr.AddrPort().Addr()), "", opts...) + pc, err = dialer.ListenPacket(ctx, "udp", "", udpAddr.AddrPort()) if err != nil { return nil, nil, err } diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index 28ebfb05..5261fefd 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -219,7 +219,7 @@ func (v *Vless) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d return NewConn(c, v), nil } - return v.DialContextWithDialer(ctx, dialer.Dialer{Options: v.Base.DialOptions(opts...)}, metadata) + return v.DialContextWithDialer(ctx, dialer.NewDialer(v.Base.DialOptions(opts...)...), metadata) } // DialContextWithDialer implements C.ProxyAdapter @@ -267,7 +267,7 @@ func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o return v.ListenPacketOnStreamConn(c, metadata) } - return v.ListenPacketWithDialer(ctx, dialer.Dialer{Options: v.Base.DialOptions(opts...)}, metadata) + return v.ListenPacketWithDialer(ctx, dialer.NewDialer(v.Base.DialOptions(opts...)...), metadata) } // ListenPacketWithDialer implements C.ProxyAdapter diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index 5697e056..2d1bd94d 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -232,7 +232,7 @@ func (v *Vmess) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d return NewConn(c, v), nil } - return v.DialContextWithDialer(ctx, dialer.Dialer{Options: v.Base.DialOptions(opts...)}, metadata) + return v.DialContextWithDialer(ctx, dialer.NewDialer(v.Base.DialOptions(opts...)...), metadata) } // DialContextWithDialer implements C.ProxyAdapter @@ -300,7 +300,7 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o }() c, err = v.StreamConn(c, metadata) - return v.ListenPacketWithDialer(ctx, dialer.Dialer{Options: v.Base.DialOptions(opts...)}, metadata) + return v.ListenPacketWithDialer(ctx, dialer.NewDialer(v.Base.DialOptions(opts...)...), metadata) } // ListenPacketWithDialer implements C.ProxyAdapter diff --git a/adapter/outboundgroup/relay.go b/adapter/outboundgroup/relay.go index 8205ee52..43ef81c9 100644 --- a/adapter/outboundgroup/relay.go +++ b/adapter/outboundgroup/relay.go @@ -59,7 +59,7 @@ func (r *Relay) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d return proxies[0].DialContext(ctx, metadata, r.Base.DialOptions(opts...)...) } var d C.Dialer - d = dialer.Dialer{Options: r.Base.DialOptions(opts...)} + d = dialer.NewDialer(r.Base.DialOptions(opts...)...) for _, proxy := range proxies[:len(proxies)-1] { d = proxyDialer{ proxy: proxy, @@ -93,7 +93,7 @@ func (r *Relay) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o } var d C.Dialer - d = dialer.Dialer{Options: r.Base.DialOptions(opts...)} + d = dialer.NewDialer(r.Base.DialOptions(opts...)...) for _, proxy := range proxies[:len(proxies)-1] { d = proxyDialer{ proxy: proxy, diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 256ff495..027b25b9 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -445,14 +445,19 @@ func concurrentIPv6DialContext(ctx context.Context, network, address string, opt return concurrentDialContext(ctx, network, ips, port, opt) } -type Dialer struct { - Options []Option +type dialer struct { + opt option } -func (d Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { - return DialContext(ctx, network, address, d.Options...) +func (d dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { + return DialContext(ctx, network, address, withOption(d.opt)) } -func (d Dialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) { - return ListenPacket(ctx, ParseNetwork(network, rAddrPort.Addr()), address, d.Options...) +func (d dialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) { + return ListenPacket(ctx, ParseNetwork(network, rAddrPort.Addr()), address, withOption(d.opt)) +} + +func NewDialer(options ...Option) dialer { + opt := ApplyOptions(options...) + return dialer{opt: *opt} } diff --git a/component/dialer/options.go b/component/dialer/options.go index 98d0b8bd..8cd6fd39 100644 --- a/component/dialer/options.go +++ b/component/dialer/options.go @@ -68,3 +68,9 @@ func WithOnlySingleStack(isIPv4 bool) Option { } } } + +func withOption(o option) Option { + return func(opt *option) { + *opt = o + } +} diff --git a/transport/tuic/client.go b/transport/tuic/client.go index dcb4e3aa..418c4133 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -28,6 +28,7 @@ var ( ) type DialFunc func(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) +type DialWithDialerFunc func(ctx context.Context, dialer C.Dialer) (pc net.PacketConn, addr net.Addr, err error) type ClientOption struct { TlsConfig *tls.Config diff --git a/transport/tuic/pool_client.go b/transport/tuic/pool_client.go index 9753da0d..304cc92d 100644 --- a/transport/tuic/pool_client.go +++ b/transport/tuic/pool_client.go @@ -37,9 +37,25 @@ func (t *PoolClient) DialContext(ctx context.Context, metadata *C.Metadata, dial newDialFn := func(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) { return t.dial(ctx, dialFn, opts...) } - conn, err := t.getClient(false, opts...).DialContext(ctx, metadata, newDialFn, opts...) + var o any = *dialer.ApplyOptions(opts...) + conn, err := t.getClient(false, o).DialContext(ctx, metadata, newDialFn, opts...) if errors.Is(err, TooManyOpenStreams) { - conn, err = t.newClient(false, opts...).DialContext(ctx, metadata, newDialFn, opts...) + conn, err = t.newClient(false, o).DialContext(ctx, metadata, newDialFn, opts...) + } + if err != nil { + return nil, err + } + return N.NewRefConn(conn, t), err +} + +func (t *PoolClient) DialContextWithDialer(ctx context.Context, d C.Dialer, metadata *C.Metadata, dialFn DialWithDialerFunc) (net.Conn, error) { + newDialFn := func(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) { + return dialFn(ctx, d) + } + var o any = d + conn, err := t.getClient(false, o).DialContext(ctx, metadata, newDialFn) + if errors.Is(err, TooManyOpenStreams) { + conn, err = t.newClient(false, o).DialContext(ctx, metadata, newDialFn) } if err != nil { return nil, err @@ -51,9 +67,25 @@ func (t *PoolClient) ListenPacketContext(ctx context.Context, metadata *C.Metada newDialFn := func(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) { return t.dial(ctx, dialFn, opts...) } - pc, err := t.getClient(true, opts...).ListenPacketContext(ctx, metadata, newDialFn, opts...) + var o any = *dialer.ApplyOptions(opts...) + pc, err := t.getClient(true, o).ListenPacketContext(ctx, metadata, newDialFn, opts...) if errors.Is(err, TooManyOpenStreams) { - pc, err = t.newClient(false, opts...).ListenPacketContext(ctx, metadata, newDialFn, opts...) + pc, err = t.newClient(true, o).ListenPacketContext(ctx, metadata, newDialFn, opts...) + } + if err != nil { + return nil, err + } + return N.NewRefPacketConn(pc, t), nil +} + +func (t *PoolClient) ListenPacketWithDialer(ctx context.Context, d C.Dialer, metadata *C.Metadata, dialFn DialWithDialerFunc) (net.PacketConn, error) { + newDialFn := func(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) { + return dialFn(ctx, d) + } + var o any = d + pc, err := t.getClient(true, o).ListenPacketContext(ctx, metadata, newDialFn) + if errors.Is(err, TooManyOpenStreams) { + pc, err = t.newClient(true, o).ListenPacketContext(ctx, metadata, newDialFn) } if err != nil { return nil, err @@ -96,7 +128,7 @@ func (t *PoolClient) forceClose() { } } -func (t *PoolClient) newClient(udp bool, opts ...dialer.Option) *Client { +func (t *PoolClient) newClient(udp bool, o any) *Client { clients := t.tcpClients clientsMutex := t.tcpClientsMutex if udp { @@ -104,8 +136,6 @@ func (t *PoolClient) newClient(udp bool, opts ...dialer.Option) *Client { clientsMutex = t.udpClientsMutex } - var o any = *dialer.ApplyOptions(opts...) - clientsMutex.Lock() defer clientsMutex.Unlock() @@ -117,15 +147,13 @@ func (t *PoolClient) newClient(udp bool, opts ...dialer.Option) *Client { return client } -func (t *PoolClient) getClient(udp bool, opts ...dialer.Option) *Client { +func (t *PoolClient) getClient(udp bool, o any) *Client { clients := t.tcpClients clientsMutex := t.tcpClientsMutex if udp { clients = t.udpClients clientsMutex = t.udpClientsMutex } - - var o any = *dialer.ApplyOptions(opts...) var bestClient *Client func() { @@ -164,7 +192,7 @@ func (t *PoolClient) getClient(udp bool, opts ...dialer.Option) *Client { } if bestClient == nil { - return t.newClient(udp, opts...) + return t.newClient(udp, o) } else { bestClient.lastVisited = time.Now() return bestClient From 63922f86a228b3240732bfe30a902b0b11b134bc Mon Sep 17 00:00:00 2001 From: embeddedlove <118641459+embeddedlove@users.noreply.github.com> Date: Thu, 22 Dec 2022 08:45:11 +0800 Subject: [PATCH 240/250] chore: linux ipv6 REDIRECT (#311) --- listener/redir/tcp_linux.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/listener/redir/tcp_linux.go b/listener/redir/tcp_linux.go index c4a47d8e..949ca717 100644 --- a/listener/redir/tcp_linux.go +++ b/listener/redir/tcp_linux.go @@ -29,6 +29,9 @@ func parserPacket(conn net.Conn) (socks5.Addr, error) { rc.Control(func(fd uintptr) { addr, err = getorigdst(fd) + if err != nil { + addr, err = getorigdst6(fd) + } }) return addr, err @@ -49,3 +52,17 @@ func getorigdst(fd uintptr) (socks5.Addr, error) { addr[1+net.IPv4len], addr[1+net.IPv4len+1] = port[0], port[1] return addr, nil } + +func getorigdst6(fd uintptr) (socks5.Addr, error) { + raw := syscall.RawSockaddrInet6{} + siz := uint32(unsafe.Sizeof(raw)) + if err := socketcall(GETSOCKOPT, fd, syscall.IPPROTO_IPV6, IP6T_SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&raw)), uintptr(unsafe.Pointer(&siz)), 0); err != nil { + return nil, err + } + addr := make([]byte, 1+net.IPv6len+2) + addr[0] = socks5.AtypIPv6 + copy(addr[1:1+net.IPv6len], raw.Addr[:]) + port := (*[2]byte)(unsafe.Pointer(&raw.Port)) // big-endian + addr[1+net.IPv6len], addr[1+net.IPv6len+1] = port[0], port[1] + return addr, nil +} From 980454beb2c778e4303739ac1c2069aec7592e90 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 22 Dec 2022 09:53:11 +0800 Subject: [PATCH 241/250] chore: cleanup code --- adapter/outbound/tuic.go | 18 ++------- component/dialer/dialer.go | 22 +++++----- component/dialer/options.go | 2 +- transport/tuic/client.go | 26 ++++++------ transport/tuic/pool_client.go | 75 ++++++++--------------------------- 5 files changed, 45 insertions(+), 98 deletions(-) diff --git a/adapter/outbound/tuic.go b/adapter/outbound/tuic.go index 6ffc0095..fa24ae39 100644 --- a/adapter/outbound/tuic.go +++ b/adapter/outbound/tuic.go @@ -55,17 +55,12 @@ type TuicOption struct { // DialContext implements C.ProxyAdapter func (t *Tuic) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) { - opts = t.Base.DialOptions(opts...) - conn, err := t.client.DialContext(ctx, metadata, t.dial, opts...) - if err != nil { - return nil, err - } - return NewConn(conn, t), err + return t.DialContextWithDialer(ctx, dialer.NewDialer(t.Base.DialOptions(opts...)...), metadata) } // DialContextWithDialer implements C.ProxyAdapter func (t *Tuic) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (C.Conn, error) { - conn, err := t.client.DialContextWithDialer(ctx, dialer, metadata, t.dialWithDialer) + conn, err := t.client.DialContextWithDialer(ctx, metadata, dialer, t.dialWithDialer) if err != nil { return nil, err } @@ -74,17 +69,12 @@ func (t *Tuic) DialContextWithDialer(ctx context.Context, dialer C.Dialer, metad // ListenPacketContext implements C.ProxyAdapter func (t *Tuic) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (_ C.PacketConn, err error) { - opts = t.Base.DialOptions(opts...) - pc, err := t.client.ListenPacketContext(ctx, metadata, t.dial, opts...) - if err != nil { - return nil, err - } - return newPacketConn(pc, t), nil + return t.ListenPacketWithDialer(ctx, dialer.NewDialer(t.Base.DialOptions(opts...)...), metadata) } // ListenPacketWithDialer implements C.ProxyAdapter func (t *Tuic) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, metadata *C.Metadata) (_ C.PacketConn, err error) { - pc, err := t.client.ListenPacketWithDialer(ctx, dialer, metadata, t.dialWithDialer) + pc, err := t.client.ListenPacketWithDialer(ctx, metadata, dialer, t.dialWithDialer) if err != nil { return nil, err } diff --git a/component/dialer/dialer.go b/component/dialer/dialer.go index 027b25b9..d7c0f3db 100644 --- a/component/dialer/dialer.go +++ b/component/dialer/dialer.go @@ -36,7 +36,7 @@ func ParseNetwork(network string, addr netip.Addr) string { return network } -func ApplyOptions(options ...Option) *option { +func applyOptions(options ...Option) *option { opt := &option{ interfaceName: DefaultInterface.Load(), routingMark: int(DefaultRoutingMark.Load()), @@ -54,7 +54,7 @@ func ApplyOptions(options ...Option) *option { } func DialContext(ctx context.Context, network, address string, options ...Option) (net.Conn, error) { - opt := ApplyOptions(options...) + opt := applyOptions(options...) if opt.network == 4 || opt.network == 6 { if strings.Contains(network, "tcp") { @@ -445,19 +445,19 @@ func concurrentIPv6DialContext(ctx context.Context, network, address string, opt return concurrentDialContext(ctx, network, ips, port, opt) } -type dialer struct { - opt option +type Dialer struct { + Opt option } -func (d dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { - return DialContext(ctx, network, address, withOption(d.opt)) +func (d Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) { + return DialContext(ctx, network, address, WithOption(d.Opt)) } -func (d dialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) { - return ListenPacket(ctx, ParseNetwork(network, rAddrPort.Addr()), address, withOption(d.opt)) +func (d Dialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) { + return ListenPacket(ctx, ParseNetwork(network, rAddrPort.Addr()), address, WithOption(d.Opt)) } -func NewDialer(options ...Option) dialer { - opt := ApplyOptions(options...) - return dialer{opt: *opt} +func NewDialer(options ...Option) Dialer { + opt := applyOptions(options...) + return Dialer{Opt: *opt} } diff --git a/component/dialer/options.go b/component/dialer/options.go index 8cd6fd39..27adc845 100644 --- a/component/dialer/options.go +++ b/component/dialer/options.go @@ -69,7 +69,7 @@ func WithOnlySingleStack(isIPv4 bool) Option { } } -func withOption(o option) Option { +func WithOption(o option) Option { return func(opt *option) { *opt = o } diff --git a/transport/tuic/client.go b/transport/tuic/client.go index 418c4133..1d1c3d15 100644 --- a/transport/tuic/client.go +++ b/transport/tuic/client.go @@ -17,7 +17,6 @@ import ( N "github.com/Dreamacro/clash/common/net" "github.com/Dreamacro/clash/common/pool" - "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" ) @@ -27,8 +26,7 @@ var ( TooManyOpenStreams = errors.New("tuic: too many open streams") ) -type DialFunc func(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) -type DialWithDialerFunc func(ctx context.Context, dialer C.Dialer) (pc net.PacketConn, addr net.Addr, err error) +type DialFunc func(ctx context.Context, dialer C.Dialer) (pc net.PacketConn, addr net.Addr, err error) type ClientOption struct { TlsConfig *tls.Config @@ -57,17 +55,17 @@ type clientImpl struct { udpInputMap sync.Map // only ready for PoolClient - optionRef any + dialerRef C.Dialer lastVisited time.Time } -func (t *clientImpl) getQuicConn(ctx context.Context, dialFn DialFunc, opts ...dialer.Option) (quic.Connection, error) { +func (t *clientImpl) getQuicConn(ctx context.Context, dialer C.Dialer, dialFn DialFunc) (quic.Connection, error) { t.connMutex.Lock() defer t.connMutex.Unlock() if t.quicConn != nil { return t.quicConn, nil } - pc, addr, err := dialFn(ctx, opts...) + pc, addr, err := dialFn(ctx, dialer) if err != nil { return nil, err } @@ -242,8 +240,8 @@ func (t *clientImpl) Close() { } } -func (t *clientImpl) DialContext(ctx context.Context, metadata *C.Metadata, dialFn DialFunc, opts ...dialer.Option) (net.Conn, error) { - quicConn, err := t.getQuicConn(ctx, dialFn, opts...) +func (t *clientImpl) DialContextWithDialer(ctx context.Context, metadata *C.Metadata, dialer C.Dialer, dialFn DialFunc) (net.Conn, error) { + quicConn, err := t.getQuicConn(ctx, dialer, dialFn) if err != nil { return nil, err } @@ -340,8 +338,8 @@ func (conn *earlyConn) Read(b []byte) (n int, err error) { return conn.BufferedConn.Read(b) } -func (t *clientImpl) ListenPacketContext(ctx context.Context, metadata *C.Metadata, dialFn DialFunc, opts ...dialer.Option) (net.PacketConn, error) { - quicConn, err := t.getQuicConn(ctx, dialFn, opts...) +func (t *clientImpl) ListenPacketWithDialer(ctx context.Context, metadata *C.Metadata, dialer C.Dialer, dialFn DialFunc) (net.PacketConn, error) { + quicConn, err := t.getQuicConn(ctx, dialer, dialFn) if err != nil { return nil, err } @@ -385,16 +383,16 @@ type Client struct { *clientImpl // use an independent pointer to let Finalizer can work no matter somewhere handle an influence in clientImpl inner } -func (t *Client) DialContext(ctx context.Context, metadata *C.Metadata, dialFn DialFunc, opts ...dialer.Option) (net.Conn, error) { - conn, err := t.clientImpl.DialContext(ctx, metadata, dialFn, opts...) +func (t *Client) DialContextWithDialer(ctx context.Context, metadata *C.Metadata, dialer C.Dialer, dialFn DialFunc) (net.Conn, error) { + conn, err := t.clientImpl.DialContextWithDialer(ctx, metadata, dialer, dialFn) if err != nil { return nil, err } return N.NewRefConn(conn, t), err } -func (t *Client) ListenPacketContext(ctx context.Context, metadata *C.Metadata, dialFn DialFunc, opts ...dialer.Option) (net.PacketConn, error) { - pc, err := t.clientImpl.ListenPacketContext(ctx, metadata, dialFn, opts...) +func (t *Client) ListenPacketWithDialer(ctx context.Context, metadata *C.Metadata, dialer C.Dialer, dialFn DialFunc) (net.PacketConn, error) { + pc, err := t.clientImpl.ListenPacketWithDialer(ctx, metadata, dialer, dialFn) if err != nil { return nil, err } diff --git a/transport/tuic/pool_client.go b/transport/tuic/pool_client.go index 304cc92d..fe06c2f3 100644 --- a/transport/tuic/pool_client.go +++ b/transport/tuic/pool_client.go @@ -10,7 +10,6 @@ import ( "github.com/Dreamacro/clash/common/generics/list" N "github.com/Dreamacro/clash/common/net" - "github.com/Dreamacro/clash/component/dialer" C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/log" ) @@ -25,7 +24,7 @@ type PoolClient struct { *ClientOption newClientOption *ClientOption - dialResultMap map[any]dialResult + dialResultMap map[C.Dialer]dialResult dialResultMutex *sync.Mutex tcpClients *list.List[*Client] tcpClientsMutex *sync.Mutex @@ -33,14 +32,10 @@ type PoolClient struct { udpClientsMutex *sync.Mutex } -func (t *PoolClient) DialContext(ctx context.Context, metadata *C.Metadata, dialFn DialFunc, opts ...dialer.Option) (net.Conn, error) { - newDialFn := func(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) { - return t.dial(ctx, dialFn, opts...) - } - var o any = *dialer.ApplyOptions(opts...) - conn, err := t.getClient(false, o).DialContext(ctx, metadata, newDialFn, opts...) +func (t *PoolClient) DialContextWithDialer(ctx context.Context, metadata *C.Metadata, dialer C.Dialer, dialFn DialFunc) (net.Conn, error) { + conn, err := t.getClient(false, dialer).DialContextWithDialer(ctx, metadata, dialer, dialFn) if errors.Is(err, TooManyOpenStreams) { - conn, err = t.newClient(false, o).DialContext(ctx, metadata, newDialFn, opts...) + conn, err = t.newClient(false, dialer).DialContextWithDialer(ctx, metadata, dialer, dialFn) } if err != nil { return nil, err @@ -48,29 +43,10 @@ func (t *PoolClient) DialContext(ctx context.Context, metadata *C.Metadata, dial return N.NewRefConn(conn, t), err } -func (t *PoolClient) DialContextWithDialer(ctx context.Context, d C.Dialer, metadata *C.Metadata, dialFn DialWithDialerFunc) (net.Conn, error) { - newDialFn := func(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) { - return dialFn(ctx, d) - } - var o any = d - conn, err := t.getClient(false, o).DialContext(ctx, metadata, newDialFn) +func (t *PoolClient) ListenPacketWithDialer(ctx context.Context, metadata *C.Metadata, dialer C.Dialer, dialFn DialFunc) (net.PacketConn, error) { + pc, err := t.getClient(true, dialer).ListenPacketWithDialer(ctx, metadata, dialer, dialFn) if errors.Is(err, TooManyOpenStreams) { - conn, err = t.newClient(false, o).DialContext(ctx, metadata, newDialFn) - } - if err != nil { - return nil, err - } - return N.NewRefConn(conn, t), err -} - -func (t *PoolClient) ListenPacketContext(ctx context.Context, metadata *C.Metadata, dialFn DialFunc, opts ...dialer.Option) (net.PacketConn, error) { - newDialFn := func(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) { - return t.dial(ctx, dialFn, opts...) - } - var o any = *dialer.ApplyOptions(opts...) - pc, err := t.getClient(true, o).ListenPacketContext(ctx, metadata, newDialFn, opts...) - if errors.Is(err, TooManyOpenStreams) { - pc, err = t.newClient(true, o).ListenPacketContext(ctx, metadata, newDialFn, opts...) + pc, err = t.newClient(true, dialer).ListenPacketWithDialer(ctx, metadata, dialer, dialFn) } if err != nil { return nil, err @@ -78,32 +54,15 @@ func (t *PoolClient) ListenPacketContext(ctx context.Context, metadata *C.Metada return N.NewRefPacketConn(pc, t), nil } -func (t *PoolClient) ListenPacketWithDialer(ctx context.Context, d C.Dialer, metadata *C.Metadata, dialFn DialWithDialerFunc) (net.PacketConn, error) { - newDialFn := func(ctx context.Context, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) { - return dialFn(ctx, d) - } - var o any = d - pc, err := t.getClient(true, o).ListenPacketContext(ctx, metadata, newDialFn) - if errors.Is(err, TooManyOpenStreams) { - pc, err = t.newClient(true, o).ListenPacketContext(ctx, metadata, newDialFn) - } - if err != nil { - return nil, err - } - return N.NewRefPacketConn(pc, t), nil -} - -func (t *PoolClient) dial(ctx context.Context, dialFn DialFunc, opts ...dialer.Option) (pc net.PacketConn, addr net.Addr, err error) { - var o any = *dialer.ApplyOptions(opts...) - +func (t *PoolClient) dial(ctx context.Context, dialer C.Dialer, dialFn DialFunc) (pc net.PacketConn, addr net.Addr, err error) { t.dialResultMutex.Lock() - dr, ok := t.dialResultMap[o] + dr, ok := t.dialResultMap[dialer] t.dialResultMutex.Unlock() if ok { return dr.pc, dr.addr, dr.err } - pc, addr, err = dialFn(ctx, opts...) + pc, addr, err = dialFn(ctx, dialer) if err != nil { return nil, nil, err } @@ -111,7 +70,7 @@ func (t *PoolClient) dial(ctx context.Context, dialFn DialFunc, opts ...dialer.O dr.pc, dr.addr, dr.err = pc, addr, err t.dialResultMutex.Lock() - t.dialResultMap[o] = dr + t.dialResultMap[dialer] = dr t.dialResultMutex.Unlock() return pc, addr, err } @@ -128,7 +87,7 @@ func (t *PoolClient) forceClose() { } } -func (t *PoolClient) newClient(udp bool, o any) *Client { +func (t *PoolClient) newClient(udp bool, dialer C.Dialer) *Client { clients := t.tcpClients clientsMutex := t.tcpClientsMutex if udp { @@ -140,14 +99,14 @@ func (t *PoolClient) newClient(udp bool, o any) *Client { defer clientsMutex.Unlock() client := NewClient(t.newClientOption, udp) - client.optionRef = o + client.dialerRef = dialer client.lastVisited = time.Now() clients.PushFront(client) return client } -func (t *PoolClient) getClient(udp bool, o any) *Client { +func (t *PoolClient) getClient(udp bool, dialer C.Dialer) *Client { clients := t.tcpClients clientsMutex := t.tcpClientsMutex if udp { @@ -167,7 +126,7 @@ func (t *PoolClient) getClient(udp bool, o any) *Client { it = next continue } - if client.optionRef == o { + if client.dialerRef == dialer { if bestClient == nil { bestClient = client } else { @@ -192,7 +151,7 @@ func (t *PoolClient) getClient(udp bool, o any) *Client { } if bestClient == nil { - return t.newClient(udp, o) + return t.newClient(udp, dialer) } else { bestClient.lastVisited = time.Now() return bestClient @@ -202,7 +161,7 @@ func (t *PoolClient) getClient(udp bool, o any) *Client { func NewPoolClient(clientOption *ClientOption) *PoolClient { p := &PoolClient{ ClientOption: clientOption, - dialResultMap: make(map[any]dialResult), + dialResultMap: make(map[C.Dialer]dialResult), dialResultMutex: &sync.Mutex{}, tcpClients: list.New[*Client](), tcpClientsMutex: &sync.Mutex{}, From a6c144038b9eef39d87a1b84e71af2a0a75043a7 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Thu, 22 Dec 2022 12:00:56 +0800 Subject: [PATCH 242/250] Chore: improve redir getorigdst --- listener/redir/tcp_linux.go | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/listener/redir/tcp_linux.go b/listener/redir/tcp_linux.go index c4a47d8e..a8b6d967 100644 --- a/listener/redir/tcp_linux.go +++ b/listener/redir/tcp_linux.go @@ -1,12 +1,16 @@ package redir import ( + "encoding/binary" "errors" "net" + "net/netip" "syscall" "unsafe" "github.com/Dreamacro/clash/transport/socks5" + + "golang.org/x/sys/unix" ) const ( @@ -25,27 +29,22 @@ func parserPacket(conn net.Conn) (socks5.Addr, error) { return nil, err } - var addr socks5.Addr + var addr netip.AddrPort rc.Control(func(fd uintptr) { addr, err = getorigdst(fd) }) - return addr, err + return socks5.AddrFromStdAddrPort(addr), err } // Call getorigdst() from linux/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c -func getorigdst(fd uintptr) (socks5.Addr, error) { - raw := syscall.RawSockaddrInet4{} - siz := uint32(unsafe.Sizeof(raw)) - if err := socketcall(GETSOCKOPT, fd, syscall.IPPROTO_IP, SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&raw)), uintptr(unsafe.Pointer(&siz)), 0); err != nil { - return nil, err +func getorigdst(fd uintptr) (netip.AddrPort, error) { + addr := unix.RawSockaddrInet4{} + size := uint32(unsafe.Sizeof(addr)) + if err := socketcall(GETSOCKOPT, fd, syscall.IPPROTO_IP, SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&addr)), uintptr(unsafe.Pointer(&size)), 0); err != nil { + return netip.AddrPort{}, err } - - addr := make([]byte, 1+net.IPv4len+2) - addr[0] = socks5.AtypIPv4 - copy(addr[1:1+net.IPv4len], raw.Addr[:]) - port := (*[2]byte)(unsafe.Pointer(&raw.Port)) // big-endian - addr[1+net.IPv4len], addr[1+net.IPv4len+1] = port[0], port[1] - return addr, nil + port := binary.BigEndian.Uint16((*(*[2]byte)(unsafe.Pointer(&addr.Port)))[:]) + return netip.AddrPortFrom(netip.AddrFrom4(addr.Addr), port), nil } From d8ac82be36303e5e9e351e0a1281bd9886f54788 Mon Sep 17 00:00:00 2001 From: igoogolx <27353191+igoogolx@users.noreply.github.com> Date: Thu, 22 Dec 2022 12:09:24 +0800 Subject: [PATCH 243/250] Fix: broken build badge (#2470) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6dff747b..0f1a05df 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@

- Github Actions + Github Actions From daf0b23805153369425ca2d6633fc88df05dd522 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 22 Dec 2022 12:31:45 +0800 Subject: [PATCH 244/250] fix: some safeConnClose forget using original --- adapter/outbound/trojan.go | 8 ++++---- adapter/outbound/vless.go | 12 ++++++------ adapter/outbound/vmess.go | 12 ++++++------ 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index e3abeab2..698db598 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -153,9 +153,9 @@ func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata, if err != nil { return nil, fmt.Errorf("%s connect error: %w", t.addr, err) } - defer func() { + defer func(c net.Conn) { safeConnClose(c, err) - }() + }(c) err = t.instance.WriteHeader(c, trojan.CommandUDP, serializesSocksAddr(metadata)) if err != nil { return nil, err @@ -173,9 +173,9 @@ func (t *Trojan) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, me if err != nil { return nil, fmt.Errorf("%s connect error: %w", t.addr, err) } - defer func() { + defer func(c net.Conn) { safeConnClose(c, err) - }() + }(c) tcpKeepAlive(c) c, err = t.plainStream(c) if err != nil { diff --git a/adapter/outbound/vless.go b/adapter/outbound/vless.go index 5261fefd..06627912 100644 --- a/adapter/outbound/vless.go +++ b/adapter/outbound/vless.go @@ -208,9 +208,9 @@ func (v *Vless) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d if err != nil { return nil, err } - defer func() { + defer func(c net.Conn) { safeConnClose(c, err) - }() + }(c) c, err = v.client.StreamConn(c, parseVlessAddr(metadata)) if err != nil { @@ -255,9 +255,9 @@ func (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o if err != nil { return nil, err } - defer func() { + defer func(c net.Conn) { safeConnClose(c, err) - }() + }(c) c, err = v.client.StreamConn(c, parseVlessAddr(metadata)) @@ -285,9 +285,9 @@ func (v *Vless) ListenPacketWithDialer(ctx context.Context, dialer C.Dialer, met return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } tcpKeepAlive(c) - defer func() { + defer func(c net.Conn) { safeConnClose(c, err) - }() + }(c) c, err = v.StreamConn(c, metadata) diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index 2d1bd94d..8d018caa 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -221,9 +221,9 @@ func (v *Vmess) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d if err != nil { return nil, err } - defer func() { + defer func(c net.Conn) { safeConnClose(c, err) - }() + }(c) c, err = v.client.DialConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) if err != nil { @@ -275,9 +275,9 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o if err != nil { return nil, err } - defer func() { + defer func(c net.Conn) { safeConnClose(c, err) - }() + }(c) if v.option.XUDP { c, err = v.client.DialXUDPPacketConn(c, M.ParseSocksaddr(metadata.RemoteAddress())) @@ -295,9 +295,9 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } tcpKeepAlive(c) - defer func() { + defer func(c net.Conn) { safeConnClose(c, err) - }() + }(c) c, err = v.StreamConn(c, metadata) return v.ListenPacketWithDialer(ctx, dialer.NewDialer(v.Base.DialOptions(opts...)...), metadata) From cdc7d449a606f83b814ee0df3674749b94b285a0 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Thu, 22 Dec 2022 12:42:38 +0800 Subject: [PATCH 245/250] Fix: safeConnClose not working (#2463) --- adapter/outbound/http.go | 4 +++- adapter/outbound/shadowsocks.go | 4 +++- adapter/outbound/shadowsocksr.go | 4 +++- adapter/outbound/snell.go | 4 +++- adapter/outbound/socks5.go | 8 ++++++-- adapter/outbound/trojan.go | 12 +++++++++--- adapter/outbound/vmess.go | 16 ++++++++++++---- 7 files changed, 39 insertions(+), 13 deletions(-) diff --git a/adapter/outbound/http.go b/adapter/outbound/http.go index 480710dc..b7425100 100644 --- a/adapter/outbound/http.go +++ b/adapter/outbound/http.go @@ -63,7 +63,9 @@ func (h *Http) DialContext(ctx context.Context, metadata *C.Metadata, opts ...di } tcpKeepAlive(c) - defer safeConnClose(c, err) + defer func(c net.Conn) { + safeConnClose(c, err) + }(c) c, err = h.StreamConn(c, metadata) if err != nil { diff --git a/adapter/outbound/shadowsocks.go b/adapter/outbound/shadowsocks.go index 6ed78050..e1878dfa 100644 --- a/adapter/outbound/shadowsocks.go +++ b/adapter/outbound/shadowsocks.go @@ -81,7 +81,9 @@ func (ss *ShadowSocks) DialContext(ctx context.Context, metadata *C.Metadata, op } tcpKeepAlive(c) - defer safeConnClose(c, err) + defer func(c net.Conn) { + safeConnClose(c, err) + }(c) c, err = ss.StreamConn(c, metadata) return NewConn(c, ss), err diff --git a/adapter/outbound/shadowsocksr.go b/adapter/outbound/shadowsocksr.go index 57ef5604..4542eebc 100644 --- a/adapter/outbound/shadowsocksr.go +++ b/adapter/outbound/shadowsocksr.go @@ -66,7 +66,9 @@ func (ssr *ShadowSocksR) DialContext(ctx context.Context, metadata *C.Metadata, } tcpKeepAlive(c) - defer safeConnClose(c, err) + defer func(c net.Conn) { + safeConnClose(c, err) + }(c) c, err = ssr.StreamConn(c, metadata) return NewConn(c, ssr), err diff --git a/adapter/outbound/snell.go b/adapter/outbound/snell.go index 07f3d89d..ab791908 100644 --- a/adapter/outbound/snell.go +++ b/adapter/outbound/snell.go @@ -80,7 +80,9 @@ func (s *Snell) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d } tcpKeepAlive(c) - defer safeConnClose(c, err) + defer func(c net.Conn) { + safeConnClose(c, err) + }(c) c, err = s.StreamConn(c, metadata) return NewConn(c, s), err diff --git a/adapter/outbound/socks5.go b/adapter/outbound/socks5.go index 3269457d..4f816211 100644 --- a/adapter/outbound/socks5.go +++ b/adapter/outbound/socks5.go @@ -69,7 +69,9 @@ func (ss *Socks5) DialContext(ctx context.Context, metadata *C.Metadata, opts .. } tcpKeepAlive(c) - defer safeConnClose(c, err) + defer func(c net.Conn) { + safeConnClose(c, err) + }(c) c, err = ss.StreamConn(c, metadata) if err != nil { @@ -95,7 +97,9 @@ func (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata, c = cc } - defer safeConnClose(c, err) + defer func(c net.Conn) { + safeConnClose(c, err) + }(c) tcpKeepAlive(c) var user *socks5.User diff --git a/adapter/outbound/trojan.go b/adapter/outbound/trojan.go index 064cd3c2..fa549df0 100644 --- a/adapter/outbound/trojan.go +++ b/adapter/outbound/trojan.go @@ -109,7 +109,9 @@ func (t *Trojan) DialContext(ctx context.Context, metadata *C.Metadata, opts ... } tcpKeepAlive(c) - defer safeConnClose(c, err) + defer func(c net.Conn) { + safeConnClose(c, err) + }(c) c, err = t.StreamConn(c, metadata) if err != nil { @@ -129,13 +131,17 @@ func (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata, if err != nil { return nil, fmt.Errorf("%s connect error: %w", t.addr, err) } - defer safeConnClose(c, err) + defer func(c net.Conn) { + safeConnClose(c, err) + }(c) } else { c, err = dialer.DialContext(ctx, "tcp", t.addr, t.Base.DialOptions(opts...)...) if err != nil { return nil, fmt.Errorf("%s connect error: %w", t.addr, err) } - defer safeConnClose(c, err) + defer func(c net.Conn) { + safeConnClose(c, err) + }(c) tcpKeepAlive(c) c, err = t.plainStream(c) if err != nil { diff --git a/adapter/outbound/vmess.go b/adapter/outbound/vmess.go index aaa9105d..8fa74fbd 100644 --- a/adapter/outbound/vmess.go +++ b/adapter/outbound/vmess.go @@ -195,7 +195,9 @@ func (v *Vmess) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d if err != nil { return nil, err } - defer safeConnClose(c, err) + defer func(c net.Conn) { + safeConnClose(c, err) + }(c) c, err = v.client.StreamConn(c, parseVmessAddr(metadata)) if err != nil { @@ -210,7 +212,9 @@ func (v *Vmess) DialContext(ctx context.Context, metadata *C.Metadata, opts ...d return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } tcpKeepAlive(c) - defer safeConnClose(c, err) + defer func(c net.Conn) { + safeConnClose(c, err) + }(c) c, err = v.StreamConn(c, metadata) return NewConn(c, v), err @@ -234,7 +238,9 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o if err != nil { return nil, err } - defer safeConnClose(c, err) + defer func(c net.Conn) { + safeConnClose(c, err) + }(c) c, err = v.client.StreamConn(c, parseVmessAddr(metadata)) } else { @@ -243,7 +249,9 @@ func (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata, o return nil, fmt.Errorf("%s connect error: %s", v.addr, err.Error()) } tcpKeepAlive(c) - defer safeConnClose(c, err) + defer func(c net.Conn) { + safeConnClose(c, err) + }(c) c, err = v.StreamConn(c, metadata) } From 4a579177833f6cec6014d8894cf985f6689b5f26 Mon Sep 17 00:00:00 2001 From: ALICE Date: Thu, 22 Dec 2022 13:30:23 +0800 Subject: [PATCH 246/250] Chore: skip cache acme challenge dns msg (#2469) --- dns/util.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/dns/util.go b/dns/util.go index df3a3331..05c734e8 100644 --- a/dns/util.go +++ b/dns/util.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "net" + "strings" "time" "github.com/Dreamacro/clash/common/cache" @@ -16,6 +17,12 @@ import ( ) func putMsgToCache(c *cache.LruCache, key string, msg *D.Msg) { + // skip dns cache for acme challenge + if q := msg.Question[0]; q.Qtype == D.TypeTXT && strings.HasPrefix(q.Name, "_acme-challenge") { + log.Debugln("[DNS] dns cache ignored because of acme challenge for: %s", q.Name) + return + } + var ttl uint32 switch { case len(msg.Answer) != 0: From fbca37c42b444429c4841983306553ffa2109cc0 Mon Sep 17 00:00:00 2001 From: embeddedlove <118641459+embeddedlove@users.noreply.github.com> Date: Thu, 22 Dec 2022 19:25:30 +0800 Subject: [PATCH 247/250] Feature: REDIRECT support IPv6 (#2473) --- listener/redir/tcp_linux.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/listener/redir/tcp_linux.go b/listener/redir/tcp_linux.go index a8b6d967..b65c34ee 100644 --- a/listener/redir/tcp_linux.go +++ b/listener/redir/tcp_linux.go @@ -32,7 +32,11 @@ func parserPacket(conn net.Conn) (socks5.Addr, error) { var addr netip.AddrPort rc.Control(func(fd uintptr) { - addr, err = getorigdst(fd) + if ip4 := c.LocalAddr().(*net.TCPAddr).IP.To4(); ip4 != nil { + addr, err = getorigdst(fd) + } else { + addr, err = getorigdst6(fd) + } }) return socks5.AddrFromStdAddrPort(addr), err @@ -48,3 +52,13 @@ func getorigdst(fd uintptr) (netip.AddrPort, error) { port := binary.BigEndian.Uint16((*(*[2]byte)(unsafe.Pointer(&addr.Port)))[:]) return netip.AddrPortFrom(netip.AddrFrom4(addr.Addr), port), nil } + +func getorigdst6(fd uintptr) (netip.AddrPort, error) { + addr := unix.RawSockaddrInet6{} + size := uint32(unsafe.Sizeof(addr)) + if err := socketcall(GETSOCKOPT, fd, syscall.IPPROTO_IPV6, IP6T_SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&addr)), uintptr(unsafe.Pointer(&size)), 0); err != nil { + return netip.AddrPort{}, err + } + port := binary.BigEndian.Uint16((*(*[2]byte)(unsafe.Pointer(&addr.Port)))[:]) + return netip.AddrPortFrom(netip.AddrFrom16(addr.Addr), port), nil +} From a03af85a6b67ac65d47f83b984f7559fe1c98fa7 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Fri, 23 Dec 2022 11:00:55 +0800 Subject: [PATCH 248/250] fix: trying to let hysteria's port hopping work --- adapter/outbound/hysteria.go | 12 +++----- transport/hysteria/conns/udp/hop.go | 39 ++++++++++++++------------ transport/hysteria/core/client.go | 6 ++-- transport/hysteria/transport/client.go | 14 ++++----- 4 files changed, 36 insertions(+), 35 deletions(-) diff --git a/adapter/outbound/hysteria.go b/adapter/outbound/hysteria.go index e019f7fa..a1276415 100644 --- a/adapter/outbound/hysteria.go +++ b/adapter/outbound/hysteria.go @@ -89,7 +89,7 @@ type HysteriaOption struct { BasicOption Name string `proxy:"name"` Server string `proxy:"server"` - Port int `proxy:"port"` + Port int `proxy:"port,omitempty"` Ports string `proxy:"ports,omitempty"` Protocol string `proxy:"protocol,omitempty"` ObfsProtocol string `proxy:"obfs-protocol,omitempty"` // compatible with Stash @@ -134,12 +134,8 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { Timeout: 8 * time.Second, }, } - var addr string - if len(option.Ports) == 0 { - addr = net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) - } else { - addr = net.JoinHostPort(option.Server, option.Ports) - } + addr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port)) + ports := option.Ports serverName := option.Server if option.SNI != "" { @@ -244,7 +240,7 @@ func NewHysteria(option HysteriaOption) (*Hysteria, error) { down = uint64(option.DownSpeed * mbpsToBps) } client, err := core.NewClient( - addr, option.Protocol, auth, tlsConfig, quicConfig, clientTransport, up, down, func(refBPS uint64) congestion.CongestionControl { + addr, ports, option.Protocol, auth, tlsConfig, quicConfig, clientTransport, up, down, func(refBPS uint64) congestion.CongestionControl { return hyCongestion.NewBrutalSender(congestion.ByteCount(refBPS)) }, obfuscator, hopInterval, option.FastOpen, ) diff --git a/transport/hysteria/conns/udp/hop.go b/transport/hysteria/conns/udp/hop.go index 4097b692..51d85ef2 100644 --- a/transport/hysteria/conns/udp/hop.go +++ b/transport/hysteria/conns/udp/hop.go @@ -58,20 +58,24 @@ type udpPacket struct { addr net.Addr } -func NewObfsUDPHopClientPacketConn(server string, hopInterval time.Duration, obfs obfs.Obfuscator, dialer utils.PacketDialer) (*ObfsUDPHopClientPacketConn, error) { - host, ports, err := parseAddr(server) +func NewObfsUDPHopClientPacketConn(server string, serverPorts string, hopInterval time.Duration, obfs obfs.Obfuscator, dialer utils.PacketDialer) (*ObfsUDPHopClientPacketConn, error) { + ports, err := parsePorts(serverPorts) if err != nil { return nil, err } // Resolve the server IP address, then attach the ports to UDP addresses - ip, err := dialer.RemoteAddr(host) + rAddr, err := dialer.RemoteAddr(server) + if err != nil { + return nil, err + } + ip, _, err := net.SplitHostPort(rAddr.String()) if err != nil { return nil, err } serverAddrs := make([]net.Addr, len(ports)) for i, port := range ports { serverAddrs[i] = &net.UDPAddr{ - IP: net.ParseIP(ip.String()), + IP: net.ParseIP(ip), Port: int(port), } } @@ -90,7 +94,7 @@ func NewObfsUDPHopClientPacketConn(server string, hopInterval time.Duration, obf }, }, } - curConn, err := dialer.ListenPacket(ip) + curConn, err := dialer.ListenPacket(rAddr) if err != nil { return nil, err } @@ -100,7 +104,7 @@ func NewObfsUDPHopClientPacketConn(server string, hopInterval time.Duration, obf conn.currentConn = curConn } go conn.recvRoutine(conn.currentConn) - go conn.hopRoutine(dialer, ip) + go conn.hopRoutine(dialer, rAddr) return conn, nil } @@ -307,29 +311,25 @@ func trySetPacketConnWriteBuffer(pc net.PacketConn, bytes int) error { return nil } -// parseAddr parses the multi-port server address and returns the host and ports. +// parsePorts parses the multi-port server address and returns the host and ports. // Supports both comma-separated single ports and dash-separated port ranges. // Format: "host:port1,port2-port3,port4" -func parseAddr(addr string) (host string, ports []uint16, err error) { - host, portStr, err := net.SplitHostPort(addr) - if err != nil { - return "", nil, err - } - portStrs := strings.Split(portStr, ",") +func parsePorts(serverPorts string) (ports []uint16, err error) { + portStrs := strings.Split(serverPorts, ",") for _, portStr := range portStrs { if strings.Contains(portStr, "-") { // Port range portRange := strings.Split(portStr, "-") if len(portRange) != 2 { - return "", nil, net.InvalidAddrError("invalid port range") + return nil, net.InvalidAddrError("invalid port range") } start, err := strconv.ParseUint(portRange[0], 10, 16) if err != nil { - return "", nil, net.InvalidAddrError("invalid port range") + return nil, net.InvalidAddrError("invalid port range") } end, err := strconv.ParseUint(portRange[1], 10, 16) if err != nil { - return "", nil, net.InvalidAddrError("invalid port range") + return nil, net.InvalidAddrError("invalid port range") } if start > end { start, end = end, start @@ -341,10 +341,13 @@ func parseAddr(addr string) (host string, ports []uint16, err error) { // Single port port, err := strconv.ParseUint(portStr, 10, 16) if err != nil { - return "", nil, net.InvalidAddrError("invalid port") + return nil, net.InvalidAddrError("invalid port") } ports = append(ports, uint16(port)) } } - return host, ports, nil + if len(ports) == 0 { + return nil, net.InvalidAddrError("invalid port") + } + return ports, nil } diff --git a/transport/hysteria/core/client.go b/transport/hysteria/core/client.go index e98a0c6b..5465e3d2 100644 --- a/transport/hysteria/core/client.go +++ b/transport/hysteria/core/client.go @@ -31,6 +31,7 @@ type CongestionFactory func(refBPS uint64) congestion.CongestionControl type Client struct { transport *transport.ClientTransport serverAddr string + serverPorts string protocol string sendBPS, recvBPS uint64 auth []byte @@ -51,13 +52,14 @@ type Client struct { fastOpen bool } -func NewClient(serverAddr string, protocol string, auth []byte, tlsConfig *tls.Config, quicConfig *quic.Config, +func NewClient(serverAddr string, serverPorts string, protocol string, auth []byte, tlsConfig *tls.Config, quicConfig *quic.Config, transport *transport.ClientTransport, sendBPS uint64, recvBPS uint64, congestionFactory CongestionFactory, obfuscator obfs.Obfuscator, hopInterval time.Duration, fastOpen bool) (*Client, error) { quicConfig.DisablePathMTUDiscovery = quicConfig.DisablePathMTUDiscovery || pmtud_fix.DisablePathMTUDiscovery c := &Client{ transport: transport, serverAddr: serverAddr, + serverPorts: serverPorts, protocol: protocol, sendBPS: sendBPS, recvBPS: recvBPS, @@ -73,7 +75,7 @@ func NewClient(serverAddr string, protocol string, auth []byte, tlsConfig *tls.C } func (c *Client) connectToServer(dialer utils.PacketDialer) error { - qs, err := c.transport.QUICDial(c.protocol, c.serverAddr, c.tlsConfig, c.quicConfig, c.obfuscator, c.hopInterval, dialer) + qs, err := c.transport.QUICDial(c.protocol, c.serverAddr, c.serverPorts, c.tlsConfig, c.quicConfig, c.obfuscator, c.hopInterval, dialer) if err != nil { return err } diff --git a/transport/hysteria/transport/client.go b/transport/hysteria/transport/client.go index af0c3ea2..e65e5016 100644 --- a/transport/hysteria/transport/client.go +++ b/transport/hysteria/transport/client.go @@ -20,7 +20,7 @@ type ClientTransport struct { Dialer *net.Dialer } -func (ct *ClientTransport) quicPacketConn(proto string, rAddr net.Addr, obfs obfsPkg.Obfuscator, hopInterval time.Duration, dialer utils.PacketDialer) (net.PacketConn, error) { +func (ct *ClientTransport) quicPacketConn(proto string, rAddr net.Addr, serverPorts string, obfs obfsPkg.Obfuscator, hopInterval time.Duration, dialer utils.PacketDialer) (net.PacketConn, error) { server := rAddr.String() if len(proto) == 0 || proto == "udp" { conn, err := dialer.ListenPacket(rAddr) @@ -28,14 +28,14 @@ func (ct *ClientTransport) quicPacketConn(proto string, rAddr net.Addr, obfs obf return nil, err } if obfs != nil { - if isMultiPortAddr(server) { - return udp.NewObfsUDPHopClientPacketConn(server, hopInterval, obfs, dialer) + if serverPorts != "" { + return udp.NewObfsUDPHopClientPacketConn(server, serverPorts, hopInterval, obfs, dialer) } oc := udp.NewObfsUDPConn(conn, obfs) return oc, nil } else { - if isMultiPortAddr(server) { - return udp.NewObfsUDPHopClientPacketConn(server, hopInterval, nil, dialer) + if serverPorts != "" { + return udp.NewObfsUDPHopClientPacketConn(server, serverPorts, hopInterval, nil, dialer) } return conn, nil } @@ -65,13 +65,13 @@ func (ct *ClientTransport) quicPacketConn(proto string, rAddr net.Addr, obfs obf } } -func (ct *ClientTransport) QUICDial(proto string, server string, tlsConfig *tls.Config, quicConfig *quic.Config, obfs obfsPkg.Obfuscator, hopInterval time.Duration, dialer utils.PacketDialer) (quic.Connection, error) { +func (ct *ClientTransport) QUICDial(proto string, server string, serverPorts string, tlsConfig *tls.Config, quicConfig *quic.Config, obfs obfsPkg.Obfuscator, hopInterval time.Duration, dialer utils.PacketDialer) (quic.Connection, error) { serverUDPAddr, err := dialer.RemoteAddr(server) if err != nil { return nil, err } - pktConn, err := ct.quicPacketConn(proto, serverUDPAddr, obfs, hopInterval, dialer) + pktConn, err := ct.quicPacketConn(proto, serverUDPAddr, serverPorts, obfs, hopInterval, dialer) if err != nil { return nil, err } From 89680de12b0cf1b1a76951239fa2cb9c54ab8d34 Mon Sep 17 00:00:00 2001 From: wwqgtxx Date: Sun, 25 Dec 2022 13:29:25 +0800 Subject: [PATCH 249/250] fix: only wrap with SyscallConn() when currentConn implements syscall.Conn --- transport/hysteria/conns/udp/hop.go | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/transport/hysteria/conns/udp/hop.go b/transport/hysteria/conns/udp/hop.go index 51d85ef2..c31f317f 100644 --- a/transport/hysteria/conns/udp/hop.go +++ b/transport/hysteria/conns/udp/hop.go @@ -58,7 +58,7 @@ type udpPacket struct { addr net.Addr } -func NewObfsUDPHopClientPacketConn(server string, serverPorts string, hopInterval time.Duration, obfs obfs.Obfuscator, dialer utils.PacketDialer) (*ObfsUDPHopClientPacketConn, error) { +func NewObfsUDPHopClientPacketConn(server string, serverPorts string, hopInterval time.Duration, obfs obfs.Obfuscator, dialer utils.PacketDialer) (net.PacketConn, error) { ports, err := parsePorts(serverPorts) if err != nil { return nil, err @@ -105,6 +105,9 @@ func NewObfsUDPHopClientPacketConn(server string, serverPorts string, hopInterva } go conn.recvRoutine(conn.currentConn) go conn.hopRoutine(dialer, rAddr) + if _, ok := conn.currentConn.(syscall.Conn); ok { + return &ObfsUDPHopClientPacketConnWithSyscall{conn}, nil + } return conn, nil } @@ -281,16 +284,6 @@ func (c *ObfsUDPHopClientPacketConn) SetWriteBuffer(bytes int) error { return trySetPacketConnWriteBuffer(c.currentConn, bytes) } -func (c *ObfsUDPHopClientPacketConn) SyscallConn() (syscall.RawConn, error) { - c.connMutex.RLock() - defer c.connMutex.RUnlock() - sc, ok := c.currentConn.(syscall.Conn) - if !ok { - return nil, errors.New("not supported") - } - return sc.SyscallConn() -} - func trySetPacketConnReadBuffer(pc net.PacketConn, bytes int) error { sc, ok := pc.(interface { SetReadBuffer(bytes int) error @@ -311,6 +304,20 @@ func trySetPacketConnWriteBuffer(pc net.PacketConn, bytes int) error { return nil } +type ObfsUDPHopClientPacketConnWithSyscall struct { + *ObfsUDPHopClientPacketConn +} + +func (c *ObfsUDPHopClientPacketConnWithSyscall) SyscallConn() (syscall.RawConn, error) { + c.connMutex.RLock() + defer c.connMutex.RUnlock() + sc, ok := c.currentConn.(syscall.Conn) + if !ok { + return nil, errors.New("not supported") + } + return sc.SyscallConn() +} + // parsePorts parses the multi-port server address and returns the host and ports. // Supports both comma-separated single ports and dash-separated port ranges. // Format: "host:port1,port2-port3,port4" From 2301b909d25587f2846f049363af5b9b4769d098 Mon Sep 17 00:00:00 2001 From: Dreamacro <8615343+Dreamacro@users.noreply.github.com> Date: Sat, 31 Dec 2022 16:30:29 +0800 Subject: [PATCH 250/250] Fix: immediately update provider when modtime too old --- adapter/provider/fetcher.go | 52 +++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/adapter/provider/fetcher.go b/adapter/provider/fetcher.go index 4a7be8b1..297e82f7 100644 --- a/adapter/provider/fetcher.go +++ b/adapter/provider/fetcher.go @@ -21,6 +21,7 @@ type parser = func([]byte) (any, error) type fetcher struct { name string vehicle types.Vehicle + interval time.Duration updatedAt *time.Time ticker *time.Ticker done chan struct{} @@ -39,15 +40,17 @@ func (f *fetcher) VehicleType() types.VehicleType { func (f *fetcher) Initial() (any, error) { var ( - buf []byte - err error - isLocal bool + buf []byte + err error + isLocal bool + immediatelyUpdate bool ) if stat, fErr := os.Stat(f.vehicle.Path()); fErr == nil { buf, err = os.ReadFile(f.vehicle.Path()) modTime := stat.ModTime() f.updatedAt = &modTime isLocal = true + immediatelyUpdate = time.Since(modTime) > f.interval } else { buf, err = f.vehicle.Read() } @@ -86,7 +89,7 @@ func (f *fetcher) Initial() (any, error) { // pull proxies automatically if f.ticker != nil { - go f.pullLoop() + go f.pullLoop(immediatelyUpdate) } return proxies, nil @@ -130,25 +133,33 @@ func (f *fetcher) Destroy() error { return nil } -func (f *fetcher) pullLoop() { +func (f *fetcher) pullLoop(immediately bool) { + update := func() { + elm, same, err := f.Update() + if err != nil { + log.Warnln("[Provider] %s pull error: %s", f.Name(), err.Error()) + return + } + + if same { + log.Debugln("[Provider] %s's proxies doesn't change", f.Name()) + return + } + + log.Infoln("[Provider] %s's proxies update", f.Name()) + if f.onUpdate != nil { + f.onUpdate(elm) + } + } + + if immediately { + update() + } + for { select { case <-f.ticker.C: - elm, same, err := f.Update() - if err != nil { - log.Warnln("[Provider] %s pull error: %s", f.Name(), err.Error()) - continue - } - - if same { - log.Debugln("[Provider] %s's proxies doesn't change", f.Name()) - continue - } - - log.Infoln("[Provider] %s's proxies update", f.Name()) - if f.onUpdate != nil { - f.onUpdate(elm) - } + update() case <-f.done: f.ticker.Stop() return @@ -178,6 +189,7 @@ func newFetcher(name string, interval time.Duration, vehicle types.Vehicle, pars name: name, ticker: ticker, vehicle: vehicle, + interval: interval, parser: parser, done: make(chan struct{}, 1), onUpdate: onUpdate,