From 13c9e3ba97e9010e0512c3b8ef023fe7aba43667 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 Jul 2024 21:37:13 +0000 Subject: [PATCH 1/9] Bump github.com/gin-contrib/cors from 1.5.0 to 1.6.0 Bumps [github.com/gin-contrib/cors](https://github.com/gin-contrib/cors) from 1.5.0 to 1.6.0. - [Release notes](https://github.com/gin-contrib/cors/releases) - [Changelog](https://github.com/gin-contrib/cors/blob/master/.goreleaser.yaml) - [Commits](https://github.com/gin-contrib/cors/compare/v1.5.0...v1.6.0) --- updated-dependencies: - dependency-name: github.com/gin-contrib/cors dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- go.mod | 14 +++++++------- go.sum | 29 ++++++++++++++--------------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/go.mod b/go.mod index 356544b..b50a1a3 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.22.2 require ( github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d - github.com/gin-contrib/cors v1.5.0 + github.com/gin-contrib/cors v1.6.0 github.com/gin-gonic/gin v1.9.1 github.com/go-gost/core v0.0.0-20240704150322-30cc92870515 github.com/go-gost/gosocks4 v0.0.1 @@ -56,7 +56,7 @@ require ( github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect github.com/alessio/shellescape v1.4.1 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/bytedance/sonic v1.10.2 // indirect + github.com/bytedance/sonic v1.11.2 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/chenzhuoyu/iasm v0.9.1 // indirect @@ -68,7 +68,7 @@ require ( github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.16.0 // indirect + github.com/go-playground/validator/v10 v10.19.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect @@ -76,9 +76,9 @@ require ( github.com/google/pprof v0.0.0-20240528025155-186aa0362fba // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/cpuid/v2 v2.2.6 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect github.com/klauspost/reedsolomon v1.11.8 // indirect - github.com/leodido/go-urn v1.2.4 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect @@ -106,10 +106,10 @@ require ( github.com/templexxx/xorsimd v0.4.2 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/ugorji/go/codec v1.2.11 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/arch v0.6.0 // indirect + golang.org/x/arch v0.7.0 // indirect golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 // indirect golang.org/x/mod v0.18.0 // indirect golang.org/x/sync v0.7.0 // indirect diff --git a/go.sum b/go.sum index 0191175..a175c88 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= -github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE= -github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= +github.com/bytedance/sonic v1.11.2 h1:ywfwo0a/3j9HR8wsYGWsIWl2mvRsI950HyoxiBERw5A= +github.com/bytedance/sonic v1.11.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -47,8 +47,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= -github.com/gin-contrib/cors v1.5.0 h1:DgGKV7DDoOn36DFkNtbHrjoRiT5ExCe+PC9/xp7aKvk= -github.com/gin-contrib/cors v1.5.0/go.mod h1:TvU7MAZ3EwrPLI2ztzTt3tqgvBCq+wn8WpZmfADjupI= +github.com/gin-contrib/cors v1.6.0 h1:0Z7D/bVhE6ja07lI8CTjTonp6SB07o8bNuFyRbsBUQg= +github.com/gin-contrib/cors v1.6.0/go.mod h1:cI+h6iOAyxKRtUtC6iF/Si1KSFvGm/gK+kshxlCi8ro= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= @@ -73,8 +73,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE= -github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= +github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= @@ -118,8 +118,8 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= -github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/reedsolomon v1.11.8 h1:s8RpUW5TK4hjr+djiOpbZJB4ksx+TdYbRH7vHQpwPOY= github.com/klauspost/reedsolomon v1.11.8/go.mod h1:4bXRN+cVzMdml6ti7qLouuYi32KHJ5MGv0Qd8a47h6A= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= @@ -127,8 +127,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= -github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -225,7 +225,6 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= @@ -238,8 +237,8 @@ github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= -github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= @@ -263,8 +262,8 @@ go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc= -golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= +golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= 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= From 949c98adc0d5d7c7c0af6bba26bb0853abcf43b8 Mon Sep 17 00:00:00 2001 From: ginuerzh Date: Mon, 8 Jul 2024 10:59:03 +0800 Subject: [PATCH 2/9] netns: add support for specifying network namespace by path --- config/parsing/service/parse.go | 10 ++++++++-- internal/net/net.go | 19 +++++++++++++++---- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/config/parsing/service/parse.go b/config/parsing/service/parse.go index f454a87..599b545 100644 --- a/config/parsing/service/parse.go +++ b/config/parsing/service/parse.go @@ -175,9 +175,15 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) { } defer netns.Set(originNs) - ns, err := netns.GetFromName(netnsIn) + var ns netns.NsHandle + + if strings.HasPrefix(netnsIn, "/") { + ns, err = netns.GetFromPath(netnsIn) + } else { + ns, err = netns.GetFromName(netnsIn) + } if err != nil { - return nil, fmt.Errorf("netns.GetFromName(%s): %v", netnsIn, err) + return nil, fmt.Errorf("netns.Get(%s): %v", netnsIn, err) } defer ns.Close() diff --git a/internal/net/net.go b/internal/net/net.go index 618552e..866253c 100644 --- a/internal/net/net.go +++ b/internal/net/net.go @@ -5,6 +5,7 @@ import ( "fmt" "net" "runtime" + "strings" "syscall" "github.com/vishvananda/netns" @@ -48,9 +49,14 @@ func (lc *ListenConfig) Listen(ctx context.Context, network, address string) (ne } defer netns.Set(originNs) - ns, err := netns.GetFromName(lc.Netns) + var ns netns.NsHandle + if strings.HasPrefix(lc.Netns, "/") { + ns, err = netns.GetFromPath(lc.Netns) + } else { + ns, err = netns.GetFromName(lc.Netns) + } if err != nil { - return nil, fmt.Errorf("netns.GetFromName(%s): %v", lc.Netns, err) + return nil, fmt.Errorf("netns.Get(%s): %v", lc.Netns, err) } defer ns.Close() @@ -73,9 +79,14 @@ func (lc *ListenConfig) ListenPacket(ctx context.Context, network, address strin } defer netns.Set(originNs) - ns, err := netns.GetFromName(lc.Netns) + var ns netns.NsHandle + if strings.HasPrefix(lc.Netns, "/") { + ns, err = netns.GetFromPath(lc.Netns) + } else { + ns, err = netns.GetFromName(lc.Netns) + } if err != nil { - return nil, fmt.Errorf("netns.GetFromName(%s): %v", lc.Netns, err) + return nil, fmt.Errorf("netns.Get(%s): %v", lc.Netns, err) } defer ns.Close() From 96f4d7bf5ce466f2ebebaae3c8a63912cd75b573 Mon Sep 17 00:00:00 2001 From: ginuerzh Date: Mon, 8 Jul 2024 10:59:32 +0800 Subject: [PATCH 3/9] netns: fix network namespaces for listeners --- connector/socks/v5/connector.go | 6 + connector/socks/v5/metadata.go | 17 +-- listener/dns/listener.go | 156 +++++++++++++++++++----- listener/dns/metadata.go | 2 + listener/dns/server.go | 38 ++++-- listener/http3/listener.go | 17 ++- listener/http3/wt/listener.go | 49 +++++--- listener/quic/listener.go | 5 +- listener/redirect/udp/listener_linux.go | 30 +++++ 9 files changed, 246 insertions(+), 74 deletions(-) diff --git a/connector/socks/v5/connector.go b/connector/socks/v5/connector.go index 5844708..d52e795 100644 --- a/connector/socks/v5/connector.go +++ b/connector/socks/v5/connector.go @@ -213,8 +213,14 @@ func (c *socks5Connector) relayUDP(ctx context.Context, conn net.Conn, addr net. cc, err := opts.NetDialer.Dial(ctx, "udp", reply.Addr.String()) if err != nil { + c.options.Logger.Error(err) return nil, err } + log.Debugf("%s <- %s -> %s", cc.LocalAddr(), cc.RemoteAddr(), addr) + + if c.md.udpTimeout > 0 { + cc.SetReadDeadline(time.Now().Add(c.md.udpTimeout)) + } return &udpRelayConn{ udpConn: cc.(*net.UDPConn), diff --git a/connector/socks/v5/metadata.go b/connector/socks/v5/metadata.go index 0ee0616..e6dba39 100644 --- a/connector/socks/v5/metadata.go +++ b/connector/socks/v5/metadata.go @@ -17,24 +17,19 @@ type metadata struct { noTLS bool relay string udpBufferSize int + udpTimeout time.Duration muxCfg *mux.Config } func (c *socks5Connector) parseMetadata(md mdata.Metadata) (err error) { - const ( - connectTimeout = "timeout" - noTLS = "notls" - relay = "relay" - udpBufferSize = "udpBufferSize" - ) - - c.md.connectTimeout = mdutil.GetDuration(md, connectTimeout) - c.md.noTLS = mdutil.GetBool(md, noTLS) - c.md.relay = mdutil.GetString(md, relay) - c.md.udpBufferSize = mdutil.GetInt(md, udpBufferSize) + c.md.connectTimeout = mdutil.GetDuration(md, "timeout") + c.md.noTLS = mdutil.GetBool(md, "notls") + c.md.relay = mdutil.GetString(md, "relay") + c.md.udpBufferSize = mdutil.GetInt(md, "udp.bufferSize", "udpBufferSize") if c.md.udpBufferSize <= 0 { c.md.udpBufferSize = defaultUDPBufferSize } + c.md.udpTimeout = mdutil.GetDuration(md, "udp.timeout") c.md.muxCfg = &mux.Config{ Version: mdutil.GetInt(md, "mux.version"), diff --git a/listener/dns/listener.go b/listener/dns/listener.go index 8f8ef17..a627679 100644 --- a/listener/dns/listener.go +++ b/listener/dns/listener.go @@ -2,6 +2,8 @@ package dns import ( "bytes" + "context" + "crypto/tls" "encoding/base64" "errors" "io" @@ -9,12 +11,12 @@ import ( "net/http" "strings" - admission "github.com/go-gost/x/admission/wrapper" - limiter "github.com/go-gost/x/limiter/traffic/wrapper" - "github.com/go-gost/core/listener" "github.com/go-gost/core/logger" md "github.com/go-gost/core/metadata" + admission "github.com/go-gost/x/admission/wrapper" + xnet "github.com/go-gost/x/internal/net" + limiter "github.com/go-gost/x/limiter/traffic/wrapper" metrics "github.com/go-gost/x/metrics/wrapper" stats "github.com/go-gost/x/observer/stats/wrapper" "github.com/go-gost/x/registry" @@ -51,48 +53,144 @@ func (l *dnsListener) Init(md md.Metadata) (err error) { return } - l.addr, err = net.ResolveTCPAddr("tcp", l.options.Addr) - if err != nil { - return err - } - switch strings.ToLower(l.md.mode) { case "tcp": - l.server = &dns.Server{ - Net: "tcp", - Addr: l.options.Addr, - Handler: l, - ReadTimeout: l.md.readTimeout, - WriteTimeout: l.md.writeTimeout, + l.addr, err = net.ResolveTCPAddr("tcp", l.options.Addr) + if err != nil { + return } + + network := "tcp" + if xnet.IsIPv4(l.options.Addr) { + network = "tcp4" + } + + lc := net.ListenConfig{} + if l.md.mptcp { + lc.SetMultipathTCP(true) + l.logger.Debugf("mptcp enabled: %v", lc.MultipathTCP()) + } + + var ln net.Listener + ln, err = lc.Listen(context.Background(), network, l.options.Addr) + if err != nil { + return + } + l.server = &dnsServer{ + server: &dns.Server{ + Net: "tcp", + Addr: l.options.Addr, + Listener: ln, + Handler: l, + ReadTimeout: l.md.readTimeout, + WriteTimeout: l.md.writeTimeout, + }, + } + case "tls": - l.server = &dns.Server{ - Net: "tcp-tls", - Addr: l.options.Addr, - Handler: l, - TLSConfig: l.options.TLSConfig, - ReadTimeout: l.md.readTimeout, - WriteTimeout: l.md.writeTimeout, + l.addr, err = net.ResolveTCPAddr("tcp", l.options.Addr) + if err != nil { + return } + + network := "tcp" + if xnet.IsIPv4(l.options.Addr) { + network = "tcp4" + } + + lc := net.ListenConfig{} + if l.md.mptcp { + lc.SetMultipathTCP(true) + l.logger.Debugf("mptcp enabled: %v", lc.MultipathTCP()) + } + + var ln net.Listener + ln, err = lc.Listen(context.Background(), network, l.options.Addr) + if err != nil { + return + } + ln = tls.NewListener(ln, l.options.TLSConfig) + + l.server = &dnsServer{ + server: &dns.Server{ + Net: "tcp-tls", + Addr: l.options.Addr, + Listener: ln, + Handler: l, + TLSConfig: l.options.TLSConfig, + ReadTimeout: l.md.readTimeout, + WriteTimeout: l.md.writeTimeout, + }, + } + case "https": + l.addr, err = net.ResolveTCPAddr("tcp", l.options.Addr) + if err != nil { + return + } + + network := "tcp" + if xnet.IsIPv4(l.options.Addr) { + network = "tcp4" + } + + lc := net.ListenConfig{} + if l.md.mptcp { + lc.SetMultipathTCP(true) + l.logger.Debugf("mptcp enabled: %v", lc.MultipathTCP()) + } + + var ln net.Listener + ln, err = lc.Listen(context.Background(), network, l.options.Addr) + if err != nil { + return + } + ln = tls.NewListener(ln, l.options.TLSConfig) + l.server = &dohServer{ addr: l.options.Addr, tlsConfig: l.options.TLSConfig, + listener: ln, server: &http.Server{ Handler: l, ReadTimeout: l.md.readTimeout, WriteTimeout: l.md.writeTimeout, }, } + default: l.addr, err = net.ResolveUDPAddr("udp", l.options.Addr) - l.server = &dns.Server{ - Net: "udp", - Addr: l.options.Addr, - Handler: l, - UDPSize: l.md.readBufferSize, - ReadTimeout: l.md.readTimeout, - WriteTimeout: l.md.writeTimeout, + if err != nil { + return + } + + network := "udp" + if xnet.IsIPv4(l.options.Addr) { + network = "udp4" + } + + lc := net.ListenConfig{} + if l.md.mptcp { + lc.SetMultipathTCP(true) + l.logger.Debugf("mptcp enabled: %v", lc.MultipathTCP()) + } + + var pc net.PacketConn + pc, err = lc.ListenPacket(context.Background(), network, l.options.Addr) + if err != nil { + return + } + + l.server = &dnsServer{ + server: &dns.Server{ + Net: "udp", + Addr: l.options.Addr, + PacketConn: pc, + Handler: l, + UDPSize: l.md.readBufferSize, + ReadTimeout: l.md.readTimeout, + WriteTimeout: l.md.writeTimeout, + }, } } @@ -104,7 +202,7 @@ func (l *dnsListener) Init(md md.Metadata) (err error) { l.errChan = make(chan error, 1) go func() { - err := l.server.ListenAndServe() + err := l.server.Serve() if err != nil { l.errChan <- err } diff --git a/listener/dns/metadata.go b/listener/dns/metadata.go index 9097f4c..187e670 100644 --- a/listener/dns/metadata.go +++ b/listener/dns/metadata.go @@ -17,6 +17,7 @@ type metadata struct { readTimeout time.Duration writeTimeout time.Duration backlog int + mptcp bool } func (l *dnsListener) parseMetadata(md mdata.Metadata) (err error) { @@ -37,6 +38,7 @@ func (l *dnsListener) parseMetadata(md mdata.Metadata) (err error) { if l.md.backlog <= 0 { l.md.backlog = defaultBacklog } + l.md.mptcp = mdutil.GetBool(md, "mptcp") return } diff --git a/listener/dns/server.go b/listener/dns/server.go index 4e60f6d..2c0c716 100644 --- a/listener/dns/server.go +++ b/listener/dns/server.go @@ -10,29 +10,47 @@ import ( "time" xnet "github.com/go-gost/x/internal/net" + "github.com/miekg/dns" ) type Server interface { - ListenAndServe() error + Serve() error Shutdown() error } +type dnsServer struct { + server *dns.Server +} + +func (s *dnsServer) Serve() error { + return s.server.ActivateAndServe() +} + +func (s *dnsServer) Shutdown() error { + return s.server.Shutdown() +} + type dohServer struct { addr string tlsConfig *tls.Config + listener net.Listener server *http.Server } -func (s *dohServer) ListenAndServe() error { - network := "tcp" - if xnet.IsIPv4(s.addr) { - network = "tcp4" +func (s *dohServer) Serve() error { + var err error + ln := s.listener + if ln == nil { + network := "tcp" + if xnet.IsIPv4(s.addr) { + network = "tcp4" + } + ln, err = net.Listen(network, s.addr) + if err != nil { + return err + } + ln = tls.NewListener(ln, s.tlsConfig) } - ln, err := net.Listen(network, s.addr) - if err != nil { - return err - } - ln = tls.NewListener(ln, s.tlsConfig) return s.server.Serve(ln) } diff --git a/listener/http3/listener.go b/listener/http3/listener.go index e263c53..4b193a8 100644 --- a/listener/http3/listener.go +++ b/listener/http3/listener.go @@ -46,11 +46,16 @@ func (l *http3Listener) Init(md md.Metadata) (err error) { return } + addr := l.options.Addr + if addr == "" { + addr = ":https" + } + network := "udp" - if xnet.IsIPv4(l.options.Addr) { + if xnet.IsIPv4(addr) { network = "udp4" } - l.addr, err = net.ResolveUDPAddr(network, l.options.Addr) + l.addr, err = net.ResolveUDPAddr(network, addr) if err != nil { return } @@ -66,15 +71,21 @@ func (l *http3Listener) Init(md md.Metadata) (err error) { quic.Version1, }, MaxIncomingStreams: int64(l.md.maxStreams), + Allow0RTT: true, }, Handler: http.HandlerFunc(l.handleFunc), } + ln, err := quic.ListenAddrEarly(addr, http3.ConfigureTLSConfig(l.server.TLSConfig), l.server.QUICConfig.Clone()) + if err != nil { + return + } + l.cqueue = make(chan net.Conn, l.md.backlog) l.errChan = make(chan error, 1) go func() { - if err := l.server.ListenAndServe(); err != nil { + if err := l.server.ServeListener(ln); err != nil { l.logger.Error(err) } }() diff --git a/listener/http3/wt/listener.go b/listener/http3/wt/listener.go index 118b10c..8a0d0a9 100644 --- a/listener/http3/wt/listener.go +++ b/listener/http3/wt/listener.go @@ -50,11 +50,22 @@ func (l *wtListener) Init(md md.Metadata) (err error) { return } + addr := l.options.Addr + if addr == "" { + addr = ":https" + } + network := "udp" - if xnet.IsIPv4(l.options.Addr) { + if xnet.IsIPv4(addr) { network = "udp4" } - l.addr, err = net.ResolveUDPAddr(network, l.options.Addr) + laddr, err := net.ResolveUDPAddr(network, addr) + if err != nil { + return + } + l.addr = laddr + + pc, err := net.ListenUDP(network, laddr) if err != nil { return } @@ -62,23 +73,25 @@ func (l *wtListener) Init(md md.Metadata) (err error) { mux := http.NewServeMux() mux.Handle(l.md.path, http.HandlerFunc(l.upgrade)) + quicCfg := &quic.Config{ + KeepAlivePeriod: l.md.keepAlivePeriod, + HandshakeIdleTimeout: l.md.handshakeTimeout, + MaxIdleTimeout: l.md.maxIdleTimeout, + /* + Versions: []quic.VersionNumber{ + quic.Version1, + quic.Version2, + }, + */ + MaxIncomingStreams: int64(l.md.maxStreams), + Allow0RTT: true, + } l.srv = &wt.Server{ H3: http3.Server{ - Addr: l.options.Addr, - TLSConfig: l.options.TLSConfig, - QUICConfig: &quic.Config{ - KeepAlivePeriod: l.md.keepAlivePeriod, - HandshakeIdleTimeout: l.md.handshakeTimeout, - MaxIdleTimeout: l.md.maxIdleTimeout, - /* - Versions: []quic.VersionNumber{ - quic.Version1, - quic.Version2, - }, - */ - MaxIncomingStreams: int64(l.md.maxStreams), - }, - Handler: mux, + Addr: l.options.Addr, + TLSConfig: l.options.TLSConfig, + QUICConfig: quicCfg, + Handler: mux, }, CheckOrigin: func(r *http.Request) bool { return true }, } @@ -87,7 +100,7 @@ func (l *wtListener) Init(md md.Metadata) (err error) { l.errChan = make(chan error, 1) go func() { - if err := l.srv.ListenAndServe(); err != nil { + if err := l.srv.Serve(pc); err != nil { l.logger.Error(err) } }() diff --git a/listener/quic/listener.go b/listener/quic/listener.go index 3604fef..1c8a070 100644 --- a/listener/quic/listener.go +++ b/listener/quic/listener.go @@ -52,11 +52,10 @@ func (l *quicListener) Init(md md.Metadata) (err error) { } network := "udp" - if xnet.IsIPv4(l.options.Addr) { + if xnet.IsIPv4(addr) { network = "udp4" } - var laddr *net.UDPAddr - laddr, err = net.ResolveUDPAddr(network, addr) + laddr, err := net.ResolveUDPAddr(network, addr) if err != nil { return } diff --git a/listener/redirect/udp/listener_linux.go b/listener/redirect/udp/listener_linux.go index b4c6051..ba2b779 100644 --- a/listener/redirect/udp/listener_linux.go +++ b/listener/redirect/udp/listener_linux.go @@ -7,12 +7,15 @@ import ( "fmt" "net" "os" + "runtime" "strconv" + "strings" "syscall" "unsafe" "github.com/go-gost/core/common/bufpool" xnet "github.com/go-gost/x/internal/net" + "github.com/vishvananda/netns" "golang.org/x/sys/unix" ) @@ -53,6 +56,33 @@ func (l *redirectListener) accept() (conn net.Conn, err error) { l.logger.Infof("%s >> %s", raddr.String(), dstAddr.String()) + if l.options.Netns != "" { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + originNs, err := netns.Get() + if err != nil { + return nil, fmt.Errorf("netns.Get(): %v", err) + } + defer netns.Set(originNs) + + var ns netns.NsHandle + + if strings.HasPrefix(l.options.Netns, "/") { + ns, err = netns.GetFromPath(l.options.Netns) + } else { + ns, err = netns.GetFromName(l.options.Netns) + } + if err != nil { + return nil, fmt.Errorf("netns.Get(%s): %v", l.options.Netns, err) + } + defer ns.Close() + + if err := netns.Set(ns); err != nil { + return nil, fmt.Errorf("netns.Set(%s): %v", l.options.Netns, err) + } + } + network := "udp" if xnet.IsIPv4(l.options.Addr) { network = "udp4" From c1d0887a9b732d75f1f27286a015d509c2cc3ec3 Mon Sep 17 00:00:00 2001 From: ginuerzh Date: Mon, 8 Jul 2024 22:38:21 +0800 Subject: [PATCH 4/9] add port range support for service --- chain/route.go | 105 +++- chain/router.go | 205 +++++++ chain/transport.go | 106 ++++ config/cmd/cmd.go | 677 ++++++++++++++++++++++++ config/parsing/node/parse.go | 3 +- config/parsing/service/parse.go | 76 ++- connector/direct/connector.go | 2 +- connector/relay/bind.go | 2 +- connector/socks/v5/bind.go | 2 +- connector/socks/v5/connector.go | 2 +- dialer/dtls/dialer.go | 2 +- dialer/grpc/dialer.go | 5 +- dialer/http2/dialer.go | 6 +- dialer/http2/h2/dialer.go | 4 +- dialer/http3/dialer.go | 2 +- dialer/http3/wt/dialer.go | 2 +- dialer/kcp/dialer.go | 2 +- dialer/mtcp/dialer.go | 2 +- dialer/mtls/dialer.go | 2 +- dialer/mws/dialer.go | 2 +- dialer/obfs/http/dialer.go | 2 +- dialer/obfs/tls/dialer.go | 2 +- dialer/pht/dialer.go | 2 +- dialer/quic/dialer.go | 2 +- dialer/ssh/dialer.go | 2 +- dialer/sshd/dialer.go | 2 +- dialer/tcp/dialer.go | 2 +- dialer/tls/dialer.go | 2 +- dialer/udp/dialer.go | 2 +- dialer/wg/dialer.go | 2 +- dialer/ws/dialer.go | 2 +- go.mod | 20 +- go.sum | 44 +- handler/dns/handler.go | 11 +- handler/forward/local/handler.go | 10 +- handler/forward/remote/handler.go | 10 +- handler/http/handler.go | 9 +- handler/http/udp.go | 2 +- handler/http2/handler.go | 9 +- handler/http3/handler.go | 8 +- handler/redirect/tcp/handler.go | 17 +- handler/redirect/udp/handler.go | 9 +- handler/relay/connect.go | 2 +- handler/relay/forward.go | 2 +- handler/relay/handler.go | 7 - handler/serial/handler.go | 13 +- handler/sni/handler.go | 11 +- handler/socks/v4/handler.go | 9 +- handler/socks/v5/connect.go | 2 +- handler/socks/v5/handler.go | 7 - handler/socks/v5/udp.go | 2 +- handler/socks/v5/udp_tun.go | 2 +- handler/ss/handler.go | 9 +- handler/ss/udp/handler.go | 9 +- handler/sshd/handler.go | 9 +- handler/tap/handler.go | 8 +- handler/tun/client.go | 2 +- handler/tun/handler.go | 6 - handler/unix/handler.go | 14 +- internal/net/addr.go | 78 +++ internal/net/dialer/dialer.go | 176 ++++++ internal/net/dialer/dialer_linux.go | 19 + internal/net/dialer/dialer_other.go | 11 + internal/net/resovle.go | 44 ++ internal/net/udp/listener.go | 232 ++++++++ internal/net/udp/pool.go | 115 ++++ listener/ftcp/listener.go | 2 +- listener/redirect/udp/listener_linux.go | 28 +- listener/rtcp/listener.go | 8 +- listener/rudp/listener.go | 8 +- listener/udp/listener.go | 2 +- resolver/exchanger/exchanger.go | 9 +- resolver/resolver.go | 5 +- 73 files changed, 1915 insertions(+), 316 deletions(-) create mode 100644 chain/router.go create mode 100644 chain/transport.go create mode 100644 config/cmd/cmd.go create mode 100644 internal/net/dialer/dialer.go create mode 100644 internal/net/dialer/dialer_linux.go create mode 100644 internal/net/dialer/dialer_other.go create mode 100644 internal/net/resovle.go create mode 100644 internal/net/udp/listener.go create mode 100644 internal/net/udp/pool.go diff --git a/chain/route.go b/chain/route.go index c915a9b..8e1de36 100644 --- a/chain/route.go +++ b/chain/route.go @@ -2,6 +2,8 @@ package chain import ( "context" + "errors" + "fmt" "net" "time" @@ -10,9 +12,86 @@ import ( "github.com/go-gost/core/logger" "github.com/go-gost/core/metrics" "github.com/go-gost/core/selector" + xnet "github.com/go-gost/x/internal/net" + "github.com/go-gost/x/internal/net/dialer" + "github.com/go-gost/x/internal/net/udp" xmetrics "github.com/go-gost/x/metrics" ) +var ( + ErrEmptyRoute = errors.New("empty route") +) + +var ( + DefaultRoute chain.Route = &defaultRoute{} +) + +// defaultRoute is a Route without nodes. +type defaultRoute struct{} + +func (*defaultRoute) Dial(ctx context.Context, network, address string, opts ...chain.DialOption) (net.Conn, error) { + var options chain.DialOptions + for _, opt := range opts { + opt(&options) + } + + netd := dialer.Dialer{ + Interface: options.Interface, + Netns: options.Netns, + Logger: options.Logger, + } + if options.SockOpts != nil { + netd.Mark = options.SockOpts.Mark + } + + return netd.Dial(ctx, network, address) +} + +func (*defaultRoute) Bind(ctx context.Context, network, address string, opts ...chain.BindOption) (net.Listener, error) { + var options chain.BindOptions + for _, opt := range opts { + opt(&options) + } + + switch network { + case "tcp", "tcp4", "tcp6": + addr, err := net.ResolveTCPAddr(network, address) + if err != nil { + return nil, err + } + return net.ListenTCP(network, addr) + case "udp", "udp4", "udp6": + addr, err := net.ResolveUDPAddr(network, address) + if err != nil { + return nil, err + } + conn, err := net.ListenUDP(network, addr) + if err != nil { + return nil, err + } + logger := logger.Default().WithFields(map[string]any{ + "network": network, + "address": address, + }) + ln := udp.NewListener(conn, &udp.ListenConfig{ + Backlog: options.Backlog, + ReadQueueSize: options.UDPDataQueueSize, + ReadBufferSize: options.UDPDataBufferSize, + TTL: options.UDPConnTTL, + KeepAlive: true, + Logger: logger, + }) + return ln, err + default: + err := fmt.Errorf("network %s unsupported", network) + return nil, err + } +} + +func (r *defaultRoute) Nodes() []*chain.Node { + return nil +} + type RouteOptions struct { Chain chain.Chainer } @@ -25,12 +104,12 @@ func ChainRouteOption(c chain.Chainer) RouteOption { } } -type route struct { +type chainRoute struct { nodes []*chain.Node options RouteOptions } -func NewRoute(opts ...RouteOption) *route { +func NewRoute(opts ...RouteOption) *chainRoute { var options RouteOptions for _, opt := range opts { if opt != nil { @@ -38,18 +117,18 @@ func NewRoute(opts ...RouteOption) *route { } } - return &route{ + return &chainRoute{ options: options, } } -func (r *route) addNode(nodes ...*chain.Node) { +func (r *chainRoute) addNode(nodes ...*chain.Node) { r.nodes = append(r.nodes, nodes...) } -func (r *route) Dial(ctx context.Context, network, address string, opts ...chain.DialOption) (net.Conn, error) { +func (r *chainRoute) Dial(ctx context.Context, network, address string, opts ...chain.DialOption) (net.Conn, error) { if len(r.Nodes()) == 0 { - return chain.DefaultRoute.Dial(ctx, network, address, opts...) + return DefaultRoute.Dial(ctx, network, address, opts...) } var options chain.DialOptions @@ -73,9 +152,9 @@ func (r *route) Dial(ctx context.Context, network, address string, opts ...chain return cc, nil } -func (r *route) Bind(ctx context.Context, network, address string, opts ...chain.BindOption) (net.Listener, error) { +func (r *chainRoute) Bind(ctx context.Context, network, address string, opts ...chain.BindOption) (net.Listener, error) { if len(r.Nodes()) == 0 { - return chain.DefaultRoute.Bind(ctx, network, address, opts...) + return DefaultRoute.Bind(ctx, network, address, opts...) } var options chain.BindOptions @@ -106,7 +185,7 @@ func (r *route) Bind(ctx context.Context, network, address string, opts ...chain return ln, nil } -func (r *route) connect(ctx context.Context, logger logger.Logger) (conn net.Conn, err error) { +func (r *chainRoute) connect(ctx context.Context, logger logger.Logger) (conn net.Conn, err error) { network := "ip" node := r.nodes[0] @@ -138,7 +217,7 @@ func (r *route) connect(ctx context.Context, logger logger.Logger) (conn net.Con } }() - addr, err := chain.Resolve(ctx, network, node.Addr, node.Options().Resolver, node.Options().HostMapper, logger) + addr, err := xnet.Resolve(ctx, network, node.Addr, node.Options().Resolver, node.Options().HostMapper, logger) marker := node.Marker() if err != nil { if marker != nil { @@ -182,7 +261,7 @@ func (r *route) connect(ctx context.Context, logger logger.Logger) (conn net.Con preNode := node for _, node := range r.nodes[1:] { marker := node.Marker() - addr, err = chain.Resolve(ctx, network, node.Addr, node.Options().Resolver, node.Options().HostMapper, logger) + addr, err = xnet.Resolve(ctx, network, node.Addr, node.Options().Resolver, node.Options().HostMapper, logger) if err != nil { cn.Close() if marker != nil { @@ -218,14 +297,14 @@ func (r *route) connect(ctx context.Context, logger logger.Logger) (conn net.Con return } -func (r *route) getNode(index int) *chain.Node { +func (r *chainRoute) getNode(index int) *chain.Node { if r == nil || len(r.Nodes()) == 0 || index < 0 || index >= len(r.Nodes()) { return nil } return r.nodes[index] } -func (r *route) Nodes() []*chain.Node { +func (r *chainRoute) Nodes() []*chain.Node { if r != nil { return r.nodes } diff --git a/chain/router.go b/chain/router.go new file mode 100644 index 0000000..1c166f3 --- /dev/null +++ b/chain/router.go @@ -0,0 +1,205 @@ +package chain + +import ( + "bytes" + "context" + "fmt" + "net" + "time" + + "github.com/go-gost/core/chain" + "github.com/go-gost/core/logger" + "github.com/go-gost/core/recorder" + xnet "github.com/go-gost/x/internal/net" +) + +type Router struct { + options chain.RouterOptions +} + +func NewRouter(opts ...chain.RouterOption) *Router { + r := &Router{} + for _, opt := range opts { + if opt != nil { + opt(&r.options) + } + } + if r.options.Timeout == 0 { + r.options.Timeout = 15 * time.Second + } + + if r.options.Logger == nil { + r.options.Logger = logger.Default().WithFields(map[string]any{"kind": "router"}) + } + return r +} + +func (r *Router) Options() *chain.RouterOptions { + if r == nil { + return nil + } + return &r.options +} + +func (r *Router) Dial(ctx context.Context, network, address string) (conn net.Conn, err error) { + if r.options.Timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, r.options.Timeout) + defer cancel() + } + + host := address + if h, _, _ := net.SplitHostPort(address); h != "" { + host = h + } + r.record(ctx, recorder.RecorderServiceRouterDialAddress, []byte(host)) + + conn, err = r.dial(ctx, network, address) + if err != nil { + r.record(ctx, recorder.RecorderServiceRouterDialAddressError, []byte(host)) + return + } + + if network == "udp" || network == "udp4" || network == "udp6" { + if _, ok := conn.(net.PacketConn); !ok { + return &packetConn{conn}, nil + } + } + return +} + +func (r *Router) record(ctx context.Context, name string, data []byte) error { + if len(data) == 0 { + return nil + } + + for _, rec := range r.options.Recorders { + if rec.Record == name { + err := rec.Recorder.Record(ctx, data) + if err != nil { + r.options.Logger.Errorf("record %s: %v", name, err) + } + return err + } + } + return nil +} + +func (r *Router) dial(ctx context.Context, network, address string) (conn net.Conn, err error) { + count := r.options.Retries + 1 + if count <= 0 { + count = 1 + } + r.options.Logger.Debugf("dial %s/%s", address, network) + + for i := 0; i < count; i++ { + var ipAddr string + ipAddr, err = xnet.Resolve(ctx, "ip", address, r.options.Resolver, r.options.HostMapper, r.options.Logger) + if err != nil { + r.options.Logger.Error(err) + break + } + + var route chain.Route + if r.options.Chain != nil { + route = r.options.Chain.Route(ctx, network, ipAddr, chain.WithHostRouteOption(address)) + } + + if r.options.Logger.IsLevelEnabled(logger.DebugLevel) { + buf := bytes.Buffer{} + for _, node := range routePath(route) { + fmt.Fprintf(&buf, "%s@%s > ", node.Name, node.Addr) + } + fmt.Fprintf(&buf, "%s", ipAddr) + r.options.Logger.Debugf("route(retry=%d) %s", i, buf.String()) + } + + if route == nil { + route = DefaultRoute + } + conn, err = route.Dial(ctx, network, ipAddr, + chain.InterfaceDialOption(r.options.IfceName), + chain.NetnsDialOption(r.options.Netns), + chain.SockOptsDialOption(r.options.SockOpts), + chain.LoggerDialOption(r.options.Logger), + ) + if err == nil { + break + } + r.options.Logger.Errorf("route(retry=%d) %s", i, err) + } + + return +} + +func (r *Router) Bind(ctx context.Context, network, address string, opts ...chain.BindOption) (ln net.Listener, err error) { + if r.options.Timeout > 0 { + var cancel context.CancelFunc + ctx, cancel = context.WithTimeout(ctx, r.options.Timeout) + defer cancel() + } + + count := r.options.Retries + 1 + if count <= 0 { + count = 1 + } + r.options.Logger.Debugf("bind on %s/%s", address, network) + + for i := 0; i < count; i++ { + var route chain.Route + if r.options.Chain != nil { + route = r.options.Chain.Route(ctx, network, address) + if route == nil || len(route.Nodes()) == 0 { + err = ErrEmptyRoute + return + } + } + + if r.options.Logger.IsLevelEnabled(logger.DebugLevel) { + buf := bytes.Buffer{} + for _, node := range routePath(route) { + fmt.Fprintf(&buf, "%s@%s > ", node.Name, node.Addr) + } + fmt.Fprintf(&buf, "%s", address) + r.options.Logger.Debugf("route(retry=%d) %s", i, buf.String()) + } + + if route == nil { + route = DefaultRoute + } + ln, err = route.Bind(ctx, network, address, opts...) + if err == nil { + break + } + r.options.Logger.Errorf("route(retry=%d) %s", i, err) + } + + return +} + +func routePath(route chain.Route) (path []*chain.Node) { + if route == nil { + return + } + for _, node := range route.Nodes() { + if tr := node.Options().Transport; tr != nil { + path = append(path, routePath(tr.Options().Route)...) + } + path = append(path, node) + } + return +} + +type packetConn struct { + net.Conn +} + +func (c *packetConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) { + n, err = c.Read(b) + addr = c.Conn.RemoteAddr() + return +} + +func (c *packetConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { + return c.Write(b) +} diff --git a/chain/transport.go b/chain/transport.go new file mode 100644 index 0000000..ed999f4 --- /dev/null +++ b/chain/transport.go @@ -0,0 +1,106 @@ +package chain + +import ( + "context" + "net" + + "github.com/go-gost/core/chain" + "github.com/go-gost/core/connector" + "github.com/go-gost/core/dialer" + net_dialer "github.com/go-gost/x/internal/net/dialer" +) + +type Transport struct { + dialer dialer.Dialer + connector connector.Connector + options chain.TransportOptions +} + +func NewTransport(d dialer.Dialer, c connector.Connector, opts ...chain.TransportOption) *Transport { + tr := &Transport{ + dialer: d, + connector: c, + } + for _, opt := range opts { + if opt != nil { + opt(&tr.options) + } + } + + return tr +} + +func (tr *Transport) Dial(ctx context.Context, addr string) (net.Conn, error) { + netd := &net_dialer.Dialer{ + Interface: tr.options.IfceName, + Netns: tr.options.Netns, + } + if tr.options.SockOpts != nil { + netd.Mark = tr.options.SockOpts.Mark + } + if tr.options.Route != nil && len(tr.options.Route.Nodes()) > 0 { + netd.DialFunc = func(ctx context.Context, network, addr string) (net.Conn, error) { + return tr.options.Route.Dial(ctx, network, addr) + } + } + opts := []dialer.DialOption{ + dialer.HostDialOption(tr.options.Addr), + dialer.NetDialerDialOption(netd), + } + return tr.dialer.Dial(ctx, addr, opts...) +} + +func (tr *Transport) Handshake(ctx context.Context, conn net.Conn) (net.Conn, error) { + var err error + if hs, ok := tr.dialer.(dialer.Handshaker); ok { + conn, err = hs.Handshake(ctx, conn, + dialer.AddrHandshakeOption(tr.options.Addr)) + if err != nil { + return nil, err + } + } + if hs, ok := tr.connector.(connector.Handshaker); ok { + return hs.Handshake(ctx, conn) + } + return conn, nil +} + +func (tr *Transport) Connect(ctx context.Context, conn net.Conn, network, address string) (net.Conn, error) { + netd := &net_dialer.Dialer{ + Interface: tr.options.IfceName, + Netns: tr.options.Netns, + } + if tr.options.SockOpts != nil { + netd.Mark = tr.options.SockOpts.Mark + } + return tr.connector.Connect(ctx, conn, network, address, + connector.DialerConnectOption(netd), + ) +} + +func (tr *Transport) Bind(ctx context.Context, conn net.Conn, network, address string, opts ...connector.BindOption) (net.Listener, error) { + if binder, ok := tr.connector.(connector.Binder); ok { + return binder.Bind(ctx, conn, network, address, opts...) + } + return nil, connector.ErrBindUnsupported +} + +func (tr *Transport) Multiplex() bool { + if mux, ok := tr.dialer.(dialer.Multiplexer); ok { + return mux.Multiplex() + } + return false +} + +func (tr *Transport) Options() *chain.TransportOptions { + if tr != nil { + return &tr.options + } + return nil +} + +func (tr *Transport) Copy() chain.Transporter { + tr2 := &Transport{} + *tr2 = *tr + return tr +} diff --git a/config/cmd/cmd.go b/config/cmd/cmd.go new file mode 100644 index 0000000..1ae66be --- /dev/null +++ b/config/cmd/cmd.go @@ -0,0 +1,677 @@ +package cmd + +import ( + "encoding/base64" + "errors" + "fmt" + "net/url" + "os" + "strconv" + "strings" + "time" + + mdutil "github.com/go-gost/core/metadata/util" + "github.com/go-gost/x/config" + xnet "github.com/go-gost/x/internal/net" + "github.com/go-gost/x/limiter/conn" + "github.com/go-gost/x/limiter/traffic" + mdx "github.com/go-gost/x/metadata" + "github.com/go-gost/x/registry" +) + +var ( + ErrInvalidCmd = errors.New("invalid cmd") + ErrInvalidNode = errors.New("invalid node") +) + +func BuildConfigFromCmd(serviceList, nodeList []string) (*config.Config, error) { + namePrefix := "" + cfg := &config.Config{} + + var chain *config.ChainConfig + if len(nodeList) > 0 { + chain = &config.ChainConfig{ + Name: fmt.Sprintf("%schain-0", namePrefix), + } + cfg.Chains = append(cfg.Chains, chain) + } + + for i, node := range nodeList { + url, err := Norm(node) + if err != nil { + return nil, err + } + + nodeConfig, err := buildNodeConfig(url) + if err != nil { + return nil, err + } + nodeConfig.Name = fmt.Sprintf("%snode-0", namePrefix) + + var nodes []*config.NodeConfig + for _, host := range strings.Split(nodeConfig.Addr, ",") { + if host == "" { + continue + } + nodeCfg := &config.NodeConfig{} + *nodeCfg = *nodeConfig + nodeCfg.Name = fmt.Sprintf("%snode-%d", namePrefix, len(nodes)) + nodeCfg.Addr = host + nodes = append(nodes, nodeCfg) + } + + m := map[string]any{} + for k, v := range url.Query() { + if len(v) > 0 { + m[k] = v[0] + } + } + md := mdx.NewMetadata(m) + + hopConfig := &config.HopConfig{ + Name: fmt.Sprintf("%shop-%d", namePrefix, i), + Selector: parseSelector(m), + Nodes: nodes, + Metadata: m, + } + + if v := mdutil.GetString(md, "bypass"); v != "" { + bypassCfg := &config.BypassConfig{ + Name: fmt.Sprintf("%sbypass-%d", namePrefix, len(cfg.Bypasses)), + } + if v[0] == '~' { + bypassCfg.Whitelist = true + v = v[1:] + } + for _, s := range strings.Split(v, ",") { + if s == "" { + continue + } + bypassCfg.Matchers = append(bypassCfg.Matchers, s) + } + hopConfig.Bypass = bypassCfg.Name + cfg.Bypasses = append(cfg.Bypasses, bypassCfg) + delete(m, "bypass") + } + if v := mdutil.GetString(md, "resolver"); v != "" { + resolverCfg := &config.ResolverConfig{ + Name: fmt.Sprintf("%sresolver-%d", namePrefix, len(cfg.Resolvers)), + } + for _, rs := range strings.Split(v, ",") { + if rs == "" { + continue + } + resolverCfg.Nameservers = append( + resolverCfg.Nameservers, + &config.NameserverConfig{ + Addr: rs, + }, + ) + } + hopConfig.Resolver = resolverCfg.Name + cfg.Resolvers = append(cfg.Resolvers, resolverCfg) + delete(m, "resolver") + } + if v := mdutil.GetString(md, "hosts"); v != "" { + hostsCfg := &config.HostsConfig{ + Name: fmt.Sprintf("%shosts-%d", namePrefix, len(cfg.Hosts)), + } + for _, s := range strings.Split(v, ",") { + ss := strings.SplitN(s, ":", 2) + if len(ss) != 2 { + continue + } + hostsCfg.Mappings = append( + hostsCfg.Mappings, + &config.HostMappingConfig{ + Hostname: ss[0], + IP: ss[1], + }, + ) + } + hopConfig.Hosts = hostsCfg.Name + cfg.Hosts = append(cfg.Hosts, hostsCfg) + delete(m, "hosts") + } + + if v := mdutil.GetString(md, "interface"); v != "" { + hopConfig.Interface = v + delete(m, "interface") + } + if v := mdutil.GetInt(md, "so_mark"); v > 0 { + hopConfig.SockOpts = &config.SockOptsConfig{ + Mark: v, + } + delete(m, "so_mark") + } + + chain.Hops = append(chain.Hops, hopConfig) + } + + var services []*config.ServiceConfig + for _, svc := range serviceList { + svc = strings.TrimSpace(svc) + if svc == "" { + continue + } + + if svc[0] == ':' || !strings.Contains(svc, "://") { + svc = "auto://" + svc + } + + host, svc := cutHost(svc) + + url, err := Norm(svc) + if err != nil { + return nil, err + } + url.Host = host + + svcs, err := buildServiceConfig(url) + if err != nil { + return nil, err + } + services = append(services, svcs...) + } + + for i, service := range services { + service.Name = fmt.Sprintf("%sservice-%d", namePrefix, i) + if chain != nil { + if service.Listener.Type == "rtcp" || service.Listener.Type == "rudp" { + service.Listener.Chain = chain.Name + } else { + service.Handler.Chain = chain.Name + } + } + cfg.Services = append(cfg.Services, service) + + mh := service.Handler.Metadata + md := mdx.NewMetadata(mh) + if v := mdutil.GetInt(md, "retries"); v > 0 { + service.Handler.Retries = v + delete(mh, "retries") + } + if v := mdutil.GetString(md, "admission"); v != "" { + admCfg := &config.AdmissionConfig{ + Name: fmt.Sprintf("%sadmission-%d", namePrefix, len(cfg.Admissions)), + } + if v[0] == '~' { + admCfg.Whitelist = true + v = v[1:] + } + for _, s := range strings.Split(v, ",") { + if s == "" { + continue + } + admCfg.Matchers = append(admCfg.Matchers, s) + } + service.Admission = admCfg.Name + cfg.Admissions = append(cfg.Admissions, admCfg) + delete(mh, "admission") + } + if v := mdutil.GetString(md, "bypass"); v != "" { + bypassCfg := &config.BypassConfig{ + Name: fmt.Sprintf("%sbypass-%d", namePrefix, len(cfg.Bypasses)), + } + if v[0] == '~' { + bypassCfg.Whitelist = true + v = v[1:] + } + for _, s := range strings.Split(v, ",") { + if s == "" { + continue + } + bypassCfg.Matchers = append(bypassCfg.Matchers, s) + } + service.Bypass = bypassCfg.Name + cfg.Bypasses = append(cfg.Bypasses, bypassCfg) + delete(mh, "bypass") + } + if v := mdutil.GetString(md, "resolver"); v != "" { + resolverCfg := &config.ResolverConfig{ + Name: fmt.Sprintf("%sresolver-%d", namePrefix, len(cfg.Resolvers)), + } + for _, rs := range strings.Split(v, ",") { + if rs == "" { + continue + } + resolverCfg.Nameservers = append( + resolverCfg.Nameservers, + &config.NameserverConfig{ + Addr: rs, + Prefer: mdutil.GetString(md, "prefer"), + }, + ) + } + service.Resolver = resolverCfg.Name + cfg.Resolvers = append(cfg.Resolvers, resolverCfg) + delete(mh, "resolver") + } + if v := mdutil.GetString(md, "hosts"); v != "" { + hostsCfg := &config.HostsConfig{ + Name: fmt.Sprintf("%shosts-%d", namePrefix, len(cfg.Hosts)), + } + for _, s := range strings.Split(v, ",") { + ss := strings.SplitN(s, ":", 2) + if len(ss) != 2 { + continue + } + hostsCfg.Mappings = append( + hostsCfg.Mappings, + &config.HostMappingConfig{ + Hostname: ss[0], + IP: ss[1], + }, + ) + } + service.Hosts = hostsCfg.Name + cfg.Hosts = append(cfg.Hosts, hostsCfg) + delete(mh, "hosts") + } + + in := mdutil.GetString(md, "limiter.in") + out := mdutil.GetString(md, "limiter.out") + cin := mdutil.GetString(md, "limiter.conn.in") + cout := mdutil.GetString(md, "limiter.conn.out") + if in != "" || cin != "" || out != "" || cout != "" { + limiter := &config.LimiterConfig{ + Name: fmt.Sprintf("%slimiter-%d", namePrefix, len(cfg.Limiters)), + } + if in != "" || out != "" { + limiter.Limits = append(limiter.Limits, + fmt.Sprintf("%s %s %s", traffic.GlobalLimitKey, in, out)) + } + if cin != "" || cout != "" { + limiter.Limits = append(limiter.Limits, + fmt.Sprintf("%s %s %s", traffic.ConnLimitKey, cin, cout)) + } + service.Limiter = limiter.Name + cfg.Limiters = append(cfg.Limiters, limiter) + delete(mh, "limiter.in") + delete(mh, "limiter.out") + delete(mh, "limiter.conn.in") + delete(mh, "limiter.conn.out") + } + + if climit := mdutil.GetInt(md, "climiter"); climit > 0 { + limiter := &config.LimiterConfig{ + Name: fmt.Sprintf("%sclimiter-%d", namePrefix, len(cfg.CLimiters)), + Limits: []string{fmt.Sprintf("%s %d", conn.GlobalLimitKey, climit)}, + } + service.CLimiter = limiter.Name + cfg.CLimiters = append(cfg.CLimiters, limiter) + delete(mh, "climiter") + } + + if rlimit := mdutil.GetFloat(md, "rlimiter"); rlimit > 0 { + limiter := &config.LimiterConfig{ + Name: fmt.Sprintf("%srlimiter-%d", namePrefix, len(cfg.RLimiters)), + Limits: []string{fmt.Sprintf("%s %s", conn.GlobalLimitKey, strconv.FormatFloat(rlimit, 'f', -1, 64))}, + } + service.RLimiter = limiter.Name + cfg.RLimiters = append(cfg.RLimiters, limiter) + delete(mh, "rlimiter") + } + } + + return cfg, nil +} + +func cutHost(s string) (host, remain string) { + if s == "" { + return + } + + n := strings.IndexByte(s, ':') + start := n + 3 + end := strings.IndexAny(s[start:], "/?") + if end < 0 { + end = len(s) + } else { + end += start + } + host = s[start:end] + remain = s[:start] + s[end:] + + return +} + +func buildServiceConfig(url *url.URL) ([]*config.ServiceConfig, error) { + namePrefix := "" + if v := os.Getenv("_GOST_ID"); v != "" { + namePrefix = fmt.Sprintf("go-%s@", v) + } + + var handler, listener string + schemes := strings.Split(url.Scheme, "+") + if len(schemes) == 1 { + handler = schemes[0] + listener = schemes[0] + } + if len(schemes) == 2 { + handler = schemes[0] + listener = schemes[1] + } + + addrs := xnet.AddrPortRange(url.Host).Addrs() + if len(addrs) == 0 { + addrs = append(addrs, url.Host) + } + + var services []*config.ServiceConfig + for _, addr := range addrs { + services = append(services, &config.ServiceConfig{ + Addr: addr, + }) + } + + if h := registry.HandlerRegistry().Get(handler); h == nil { + handler = "auto" + } + if ln := registry.ListenerRegistry().Get(listener); ln == nil { + listener = "tcp" + if handler == "ssu" { + listener = "udp" + } + } + + var nodes []*config.ForwardNodeConfig + // forward mode + if remotes := strings.Trim(url.EscapedPath(), "/"); remotes != "" { + i := 0 + for _, addr := range strings.Split(remotes, ",") { + addrs := xnet.AddrPortRange(addr).Addrs() + if len(addrs) == 0 { + addrs = append(addrs, addr) + } + for _, adr := range addrs { + nodes = append(nodes, &config.ForwardNodeConfig{ + Name: fmt.Sprintf("%starget-%d", namePrefix, i), + Addr: adr, + }) + i++ + } + } + + if handler != "relay" { + if listener == "tcp" || listener == "udp" || + listener == "rtcp" || listener == "rudp" || + listener == "tun" || listener == "tap" || + listener == "dns" || listener == "unix" || + listener == "serial" { + handler = listener + } else { + handler = "forward" + } + } + } + + if len(nodes) > 0 { + if len(services) == 1 { + services[0].Forwarder = &config.ForwarderConfig{ + Nodes: nodes, + } + } else { + for i, svc := range services { + if len(nodes) == 1 { + svc.Forwarder = &config.ForwarderConfig{ + Nodes: nodes, + } + } else { + if i < len(nodes) { + svc.Forwarder = &config.ForwarderConfig{ + Nodes: []*config.ForwardNodeConfig{nodes[i]}, + } + } + } + } + } + } + + var auth *config.AuthConfig + if url.User != nil { + auth = &config.AuthConfig{ + Username: url.User.Username(), + } + auth.Password, _ = url.User.Password() + } + + m := map[string]any{} + for k, v := range url.Query() { + if len(v) > 0 { + m[k] = v[0] + } + } + md := mdx.NewMetadata(m) + + if sa := mdutil.GetString(md, "auth"); sa != "" { + au, err := parseAuthFromCmd(sa) + if err != nil { + return nil, err + } + auth = au + } + delete(m, "auth") + + tlsConfig := &config.TLSConfig{ + CertFile: mdutil.GetString(md, "tls.certFile", "certFile", "cert"), + KeyFile: mdutil.GetString(md, "tls.keyFile", "keyFile", "key"), + CAFile: mdutil.GetString(md, "tls.caFile", "caFile", "ca"), + } + + delete(m, "tls.certFile") + delete(m, "certFile") + delete(m, "cert") + delete(m, "tls.keyFile") + delete(m, "keyFile") + delete(m, "key") + delete(m, "tls.caFile") + delete(m, "caFile") + delete(m, "ca") + + if tlsConfig.CertFile == "" { + tlsConfig = nil + } + + if v := mdutil.GetString(md, "dns"); v != "" { + md.Set("dns", strings.Split(v, ",")) + } + + selector := parseSelector(m) + handlerCfg := &config.HandlerConfig{ + Type: handler, + Auth: auth, + Metadata: m, + } + listenerCfg := &config.ListenerConfig{ + Type: listener, + TLS: tlsConfig, + Metadata: m, + } + if listenerCfg.Type == "ssh" || listenerCfg.Type == "sshd" { + handlerCfg.Auth = nil + listenerCfg.Auth = auth + } + + for _, svc := range services { + if svc.Forwarder != nil { + svc.Forwarder.Selector = selector + } + svc.Handler = handlerCfg + svc.Listener = listenerCfg + svc.Metadata = m + } + + return services, nil +} + +func buildNodeConfig(url *url.URL) (*config.NodeConfig, error) { + var connector, dialer string + schemes := strings.Split(url.Scheme, "+") + if len(schemes) == 1 { + connector = schemes[0] + dialer = schemes[0] + } + if len(schemes) == 2 { + connector = schemes[0] + dialer = schemes[1] + } + + m := map[string]any{} + for k, v := range url.Query() { + if len(v) > 0 { + m[k] = v[0] + } + } + md := mdx.NewMetadata(m) + + node := &config.NodeConfig{ + Addr: url.Host, + Metadata: m, + } + + if c := registry.ConnectorRegistry().Get(connector); c == nil { + connector = "http" + } + if d := registry.DialerRegistry().Get(dialer); d == nil { + dialer = "tcp" + if connector == "ssu" { + dialer = "udp" + } + } + + var auth *config.AuthConfig + if url.User != nil { + auth = &config.AuthConfig{ + Username: url.User.Username(), + } + auth.Password, _ = url.User.Password() + } + + if sauth := mdutil.GetString(md, "auth"); sauth != "" && auth == nil { + au, err := parseAuthFromCmd(sauth) + if err != nil { + return nil, err + } + auth = au + } + delete(m, "auth") + + tlsConfig := &config.TLSConfig{ + CertFile: mdutil.GetString(md, "tls.certFile", "certFile", "cert"), + KeyFile: mdutil.GetString(md, "tls.keyFile", "keyFile", "key"), + CAFile: mdutil.GetString(md, "tls.caFile", "caFile", "ca"), + Secure: mdutil.GetBool(md, "tls.secure", "secure"), + ServerName: mdutil.GetString(md, "tls.servername", "servername"), + } + if tlsConfig.ServerName == "" { + tlsConfig.ServerName = url.Hostname() + } + + delete(m, "tls.certFile") + delete(m, "certFile") + delete(m, "cert") + delete(m, "tls.keyFile") + delete(m, "keyFile") + delete(m, "key") + delete(m, "tls.caFile") + delete(m, "caFile") + delete(m, "ca") + delete(m, "tls.secure") + delete(m, "secure") + delete(m, "tls.servername") + delete(m, "serverName") + + if !tlsConfig.Secure && tlsConfig.CertFile == "" && tlsConfig.CAFile == "" && tlsConfig.ServerName == "" { + tlsConfig = nil + } + + node.Connector = &config.ConnectorConfig{ + Type: connector, + Auth: auth, + Metadata: m, + } + node.Dialer = &config.DialerConfig{ + Type: dialer, + TLS: tlsConfig, + Metadata: m, + } + + if node.Dialer.Type == "ssh" || node.Dialer.Type == "sshd" { + node.Connector.Auth = nil + node.Dialer.Auth = auth + } + + return node, nil +} + +func Norm(s string) (*url.URL, error) { + s = strings.TrimSpace(s) + if s == "" { + return nil, ErrInvalidCmd + } + + if s[0] == ':' || !strings.Contains(s, "://") { + s = "auto://" + s + } + + url, err := url.Parse(s) + if err != nil { + return nil, err + } + if url.Scheme == "https" { + url.Scheme = "http+tls" + } + + return url, nil +} + +func parseAuthFromCmd(sa string) (*config.AuthConfig, error) { + v, err := base64.StdEncoding.DecodeString(sa) + if err != nil { + return nil, err + } + cs := string(v) + n := strings.IndexByte(cs, ':') + if n < 0 { + return &config.AuthConfig{ + Username: cs, + }, nil + } + + return &config.AuthConfig{ + Username: cs[:n], + Password: cs[n+1:], + }, nil +} + +func parseSelector(m map[string]any) *config.SelectorConfig { + md := mdx.NewMetadata(m) + strategy := mdutil.GetString(md, "strategy") + maxFails := mdutil.GetInt(md, "maxFails", "max_fails") + failTimeout := mdutil.GetDuration(md, "failTimeout", "fail_timeout") + if strategy == "" && maxFails <= 0 && failTimeout <= 0 { + return nil + } + if strategy == "" { + strategy = "round" + } + if maxFails <= 0 { + maxFails = 1 + } + if failTimeout <= 0 { + failTimeout = 30 * time.Second + } + + delete(m, "strategy") + delete(m, "maxFails") + delete(m, "max_fails") + delete(m, "failTimeout") + delete(m, "fail_timeout") + + return &config.SelectorConfig{ + Strategy: strategy, + MaxFails: maxFails, + FailTimeout: failTimeout, + } +} diff --git a/config/parsing/node/parse.go b/config/parsing/node/parse.go index af99d07..41fd58a 100644 --- a/config/parsing/node/parse.go +++ b/config/parsing/node/parse.go @@ -14,6 +14,7 @@ import ( "github.com/go-gost/core/metadata" mdutil "github.com/go-gost/core/metadata/util" xauth "github.com/go-gost/x/auth" + xchain "github.com/go-gost/x/chain" "github.com/go-gost/x/config" "github.com/go-gost/x/config/parsing" auth_parser "github.com/go-gost/x/config/parsing/auth" @@ -139,7 +140,7 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No } } - tr := chain.NewTransport(d, cr, + tr := xchain.NewTransport(d, cr, chain.AddrTransportOption(cfg.Addr), chain.InterfaceTransportOption(cfg.Interface), chain.NetnsTransportOption(cfg.Netns), diff --git a/config/parsing/service/parse.go b/config/parsing/service/parse.go index 599b545..79ad47f 100644 --- a/config/parsing/service/parse.go +++ b/config/parsing/service/parse.go @@ -28,7 +28,6 @@ import ( hop_parser "github.com/go-gost/x/config/parsing/hop" logger_parser "github.com/go-gost/x/config/parsing/logger" selector_parser "github.com/go-gost/x/config/parsing/selector" - xnet "github.com/go-gost/x/internal/net" tls_util "github.com/go-gost/x/internal/util/tls" "github.com/go-gost/x/metadata" "github.com/go-gost/x/registry" @@ -151,7 +150,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) { listenOpts := []listener.Option{ listener.AddrOption(cfg.Addr), - listener.RouterOption(chain.NewRouter(routerOpts...)), + listener.RouterOption(xchain.NewRouter(routerOpts...)), listener.AutherOption(auther), listener.AuthOption(auth_parser.Info(cfg.Listener.Auth)), listener.TLSConfigOption(tlsConfig), @@ -271,7 +270,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) { var h handler.Handler if rf := registry.HandlerRegistry().Get(cfg.Handler.Type); rf != nil { h = rf( - handler.RouterOption(chain.NewRouter(routerOpts...)), + handler.RouterOption(xchain.NewRouter(routerOpts...)), handler.AutherOption(auther), handler.AuthOption(auth_parser.Info(cfg.Handler.Auth)), handler.BypassOption(bypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)), @@ -339,50 +338,41 @@ func parseForwarder(cfg *config.ForwarderConfig, log logger.Logger) (hop.Hop, er Selector: cfg.Selector, } for _, node := range cfg.Nodes { - if node != nil { - addrs := xnet.AddrPortRange(node.Addr).Addrs() - if len(addrs) == 0 { - addrs = append(addrs, node.Addr) - } - for i, addr := range addrs { - name := node.Name - if i > 0 { - name = fmt.Sprintf("%s-%d", node.Name, i) - } + if node == nil { + continue + } - filter := node.Filter - if filter == nil { - if node.Protocol != "" || node.Host != "" || node.Path != "" { - filter = &config.NodeFilterConfig{ - Protocol: node.Protocol, - Host: node.Host, - Path: node.Path, - } - } + filter := node.Filter + if filter == nil { + if node.Protocol != "" || node.Host != "" || node.Path != "" { + filter = &config.NodeFilterConfig{ + Protocol: node.Protocol, + Host: node.Host, + Path: node.Path, } - - httpCfg := node.HTTP - if node.Auth != nil { - if httpCfg == nil { - httpCfg = &config.HTTPNodeConfig{} - } - if httpCfg.Auth == nil { - httpCfg.Auth = node.Auth - } - } - hc.Nodes = append(hc.Nodes, &config.NodeConfig{ - Name: name, - Addr: addr, - Network: node.Network, - Bypass: node.Bypass, - Bypasses: node.Bypasses, - Filter: filter, - HTTP: httpCfg, - TLS: node.TLS, - Metadata: node.Metadata, - }) } } + + httpCfg := node.HTTP + if node.Auth != nil { + if httpCfg == nil { + httpCfg = &config.HTTPNodeConfig{} + } + if httpCfg.Auth == nil { + httpCfg.Auth = node.Auth + } + } + hc.Nodes = append(hc.Nodes, &config.NodeConfig{ + Name: node.Name, + Addr: node.Addr, + Network: node.Network, + Bypass: node.Bypass, + Bypasses: node.Bypasses, + Filter: filter, + HTTP: httpCfg, + TLS: node.TLS, + Metadata: node.Metadata, + }) } return hop_parser.ParseHop(&hc, log) } diff --git a/connector/direct/connector.go b/connector/direct/connector.go index e3ffd41..bba2052 100644 --- a/connector/direct/connector.go +++ b/connector/direct/connector.go @@ -39,7 +39,7 @@ func (c *directConnector) Connect(ctx context.Context, _ net.Conn, network, addr opt(&cOpts) } - conn, err := cOpts.NetDialer.Dial(ctx, network, address) + conn, err := cOpts.Dialer.Dial(ctx, network, address) if err != nil { return nil, err } diff --git a/connector/relay/bind.go b/connector/relay/bind.go index 08f279d..09a7513 100644 --- a/connector/relay/bind.go +++ b/connector/relay/bind.go @@ -6,10 +6,10 @@ import ( "net" "strconv" - "github.com/go-gost/core/common/net/udp" "github.com/go-gost/core/connector" "github.com/go-gost/core/logger" "github.com/go-gost/relay" + "github.com/go-gost/x/internal/net/udp" "github.com/go-gost/x/internal/util/mux" relay_util "github.com/go-gost/x/internal/util/relay" ) diff --git a/connector/socks/v5/bind.go b/connector/socks/v5/bind.go index 31f0ea8..b2e6d39 100644 --- a/connector/socks/v5/bind.go +++ b/connector/socks/v5/bind.go @@ -5,10 +5,10 @@ import ( "fmt" "net" - "github.com/go-gost/core/common/net/udp" "github.com/go-gost/core/connector" "github.com/go-gost/core/logger" "github.com/go-gost/gosocks5" + "github.com/go-gost/x/internal/net/udp" "github.com/go-gost/x/internal/util/mux" "github.com/go-gost/x/internal/util/socks" ) diff --git a/connector/socks/v5/connector.go b/connector/socks/v5/connector.go index d52e795..2d8ef17 100644 --- a/connector/socks/v5/connector.go +++ b/connector/socks/v5/connector.go @@ -211,7 +211,7 @@ func (c *socks5Connector) relayUDP(ctx context.Context, conn net.Conn, addr net. log.Debugf("bind on: %v", reply.Addr) - cc, err := opts.NetDialer.Dial(ctx, "udp", reply.Addr.String()) + cc, err := opts.Dialer.Dial(ctx, "udp", reply.Addr.String()) if err != nil { c.options.Logger.Error(err) return nil, err diff --git a/dialer/dtls/dialer.go b/dialer/dtls/dialer.go index 6286092..7ac9a04 100644 --- a/dialer/dtls/dialer.go +++ b/dialer/dtls/dialer.go @@ -45,7 +45,7 @@ func (d *dtlsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO opt(&options) } - conn, err := options.NetDialer.Dial(ctx, "udp", addr) + conn, err := options.Dialer.Dial(ctx, "udp", addr) if err != nil { return nil, err } diff --git a/dialer/grpc/dialer.go b/dialer/grpc/dialer.go index ea12072..7a608c4 100644 --- a/dialer/grpc/dialer.go +++ b/dialer/grpc/dialer.go @@ -71,14 +71,13 @@ func (d *grpcDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO grpcOpts := []grpc.DialOption{ // grpc.WithBlock(), grpc.WithContextDialer(func(c context.Context, s string) (net.Conn, error) { - return options.NetDialer.Dial(c, "tcp", s) + return options.Dialer.Dial(c, "tcp", s) }), grpc.WithAuthority(host), grpc.WithConnectParams(grpc.ConnectParams{ Backoff: backoff.DefaultConfig, MinConnectTimeout: d.md.minConnectTimeout, }), - grpc.FailOnNonTempDialError(true), } if !d.md.insecure { grpcOpts = append(grpcOpts, grpc.WithTransportCredentials(credentials.NewTLS(d.options.TLSConfig))) @@ -94,7 +93,7 @@ func (d *grpcDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO })) } - cc, err := grpc.DialContext(ctx, addr, grpcOpts...) + cc, err := grpc.NewClient(addr, grpcOpts...) if err != nil { d.options.Logger.Error(err) return nil, err diff --git a/dialer/http2/dialer.go b/dialer/http2/dialer.go index 369640e..0207345 100644 --- a/dialer/http2/dialer.go +++ b/dialer/http2/dialer.go @@ -7,10 +7,10 @@ import ( "sync" "time" - net_dialer "github.com/go-gost/core/common/net/dialer" "github.com/go-gost/core/dialer" "github.com/go-gost/core/logger" md "github.com/go-gost/core/metadata" + net_dialer "github.com/go-gost/x/internal/net/dialer" mdx "github.com/go-gost/x/metadata" "github.com/go-gost/x/registry" ) @@ -72,7 +72,7 @@ func (d *http2Dialer) Dial(ctx context.Context, address string, opts ...dialer.D { // Check whether the connection is established properly - netd := options.NetDialer + netd := options.Dialer if netd == nil { netd = net_dialer.DefaultNetDialer } @@ -87,7 +87,7 @@ func (d *http2Dialer) Dial(ctx context.Context, address string, opts ...dialer.D Transport: &http.Transport{ TLSClientConfig: d.options.TLSConfig, DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - netd := options.NetDialer + netd := options.Dialer if netd == nil { netd = net_dialer.DefaultNetDialer } diff --git a/dialer/http2/h2/dialer.go b/dialer/http2/h2/dialer.go index 027eeb9..63d1420 100644 --- a/dialer/http2/h2/dialer.go +++ b/dialer/http2/h2/dialer.go @@ -94,14 +94,14 @@ func (d *h2Dialer) Dial(ctx context.Context, address string, opts ...dialer.Dial client.Transport = &http2.Transport{ AllowHTTP: true, DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) { - return options.NetDialer.Dial(ctx, network, addr) + return options.Dialer.Dial(ctx, network, addr) }, } } else { client.Transport = &http.Transport{ TLSClientConfig: d.options.TLSConfig, DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - return options.NetDialer.Dial(ctx, network, addr) + return options.Dialer.Dial(ctx, network, addr) }, ForceAttemptHTTP2: true, MaxIdleConns: 100, diff --git a/dialer/http3/dialer.go b/dialer/http3/dialer.go index 6ec0deb..1b406bd 100644 --- a/dialer/http3/dialer.go +++ b/dialer/http3/dialer.go @@ -79,7 +79,7 @@ func (d *http3Dialer) Dial(ctx context.Context, addr string, opts ...dialer.Dial return nil, err } - udpConn, err := options.NetDialer.Dial(ctx, "udp", "") + udpConn, err := options.Dialer.Dial(ctx, "udp", "") if err != nil { return nil, err } diff --git a/dialer/http3/wt/dialer.go b/dialer/http3/wt/dialer.go index 696053b..3c7cd2a 100644 --- a/dialer/http3/wt/dialer.go +++ b/dialer/http3/wt/dialer.go @@ -81,7 +81,7 @@ func (d *wtDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOpt return nil, err } - udpConn, err := options.NetDialer.Dial(ctx, "udp", "") + udpConn, err := options.Dialer.Dial(ctx, "udp", "") if err != nil { return nil, err } diff --git a/dialer/kcp/dialer.go b/dialer/kcp/dialer.go index 20b993c..a73df73 100644 --- a/dialer/kcp/dialer.go +++ b/dialer/kcp/dialer.go @@ -83,7 +83,7 @@ func (d *kcpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp PacketConn: pc, } } else { - c, err := options.NetDialer.Dial(ctx, "udp", "") + c, err := options.Dialer.Dial(ctx, "udp", "") if err != nil { return nil, err } diff --git a/dialer/mtcp/dialer.go b/dialer/mtcp/dialer.go index a9d1831..7a1c5eb 100644 --- a/dialer/mtcp/dialer.go +++ b/dialer/mtcp/dialer.go @@ -67,7 +67,7 @@ func (d *mtcpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO opt(&options) } - conn, err = options.NetDialer.Dial(ctx, "tcp", addr) + conn, err = options.Dialer.Dial(ctx, "tcp", addr) if err != nil { return } diff --git a/dialer/mtls/dialer.go b/dialer/mtls/dialer.go index f7052a6..2c7b902 100644 --- a/dialer/mtls/dialer.go +++ b/dialer/mtls/dialer.go @@ -68,7 +68,7 @@ func (d *mtlsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO opt(&options) } - conn, err = options.NetDialer.Dial(ctx, "tcp", addr) + conn, err = options.Dialer.Dial(ctx, "tcp", addr) if err != nil { return } diff --git a/dialer/mws/dialer.go b/dialer/mws/dialer.go index cdd1105..9ffe7a7 100644 --- a/dialer/mws/dialer.go +++ b/dialer/mws/dialer.go @@ -82,7 +82,7 @@ func (d *mwsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp opt(&options) } - conn, err = options.NetDialer.Dial(ctx, "tcp", addr) + conn, err = options.Dialer.Dial(ctx, "tcp", addr) if err != nil { return } diff --git a/dialer/obfs/http/dialer.go b/dialer/obfs/http/dialer.go index d8b4a22..e69d148 100644 --- a/dialer/obfs/http/dialer.go +++ b/dialer/obfs/http/dialer.go @@ -55,7 +55,7 @@ func (d *obfsHTTPDialer) Dial(ctx context.Context, addr string, opts ...dialer.D opt(options) } - conn, err := options.NetDialer.Dial(ctx, "tcp", addr) + conn, err := options.Dialer.Dial(ctx, "tcp", addr) if err != nil { d.logger.Error(err) } diff --git a/dialer/obfs/tls/dialer.go b/dialer/obfs/tls/dialer.go index d7b5b1a..d3ba740 100644 --- a/dialer/obfs/tls/dialer.go +++ b/dialer/obfs/tls/dialer.go @@ -40,7 +40,7 @@ func (d *obfsTLSDialer) Dial(ctx context.Context, addr string, opts ...dialer.Di opt(options) } - conn, err := options.NetDialer.Dial(ctx, "tcp", addr) + conn, err := options.Dialer.Dial(ctx, "tcp", addr) if err != nil { d.logger.Error(err) } diff --git a/dialer/pht/dialer.go b/dialer/pht/dialer.go index 0398b2f..de69aff 100644 --- a/dialer/pht/dialer.go +++ b/dialer/pht/dialer.go @@ -87,7 +87,7 @@ func (d *phtDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp tr := &http.Transport{ // Proxy: http.ProxyFromEnvironment, DialContext: func(ctx context.Context, network, adr string) (net.Conn, error) { - return options.NetDialer.Dial(ctx, network, addr) + return options.Dialer.Dial(ctx, network, addr) }, ForceAttemptHTTP2: true, MaxIdleConns: 100, diff --git a/dialer/quic/dialer.go b/dialer/quic/dialer.go index c43f2ef..618e1b8 100644 --- a/dialer/quic/dialer.go +++ b/dialer/quic/dialer.go @@ -67,7 +67,7 @@ func (d *quicDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO opt(options) } - c, err := options.NetDialer.Dial(ctx, "udp", "") + c, err := options.Dialer.Dial(ctx, "udp", "") if err != nil { return nil, err } diff --git a/dialer/ssh/dialer.go b/dialer/ssh/dialer.go index 509c3cb..d97f000 100644 --- a/dialer/ssh/dialer.go +++ b/dialer/ssh/dialer.go @@ -64,7 +64,7 @@ func (d *sshDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp opt(&options) } - conn, err = options.NetDialer.Dial(ctx, "tcp", addr) + conn, err = options.Dialer.Dial(ctx, "tcp", addr) if err != nil { return } diff --git a/dialer/sshd/dialer.go b/dialer/sshd/dialer.go index c09c36b..1ec7760 100644 --- a/dialer/sshd/dialer.go +++ b/dialer/sshd/dialer.go @@ -64,7 +64,7 @@ func (d *sshdDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO opt(&options) } - conn, err = options.NetDialer.Dial(ctx, "tcp", addr) + conn, err = options.Dialer.Dial(ctx, "tcp", addr) if err != nil { return } diff --git a/dialer/tcp/dialer.go b/dialer/tcp/dialer.go index 16f19f3..91c9e60 100644 --- a/dialer/tcp/dialer.go +++ b/dialer/tcp/dialer.go @@ -40,7 +40,7 @@ func (d *tcpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp opt(&options) } - conn, err := options.NetDialer.Dial(ctx, "tcp", addr) + conn, err := options.Dialer.Dial(ctx, "tcp", addr) if err != nil { d.logger.Error(err) } diff --git a/dialer/tls/dialer.go b/dialer/tls/dialer.go index fd9967c..a5f68d3 100644 --- a/dialer/tls/dialer.go +++ b/dialer/tls/dialer.go @@ -44,7 +44,7 @@ func (d *tlsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp opt(&options) } - conn, err := options.NetDialer.Dial(ctx, "tcp", addr) + conn, err := options.Dialer.Dial(ctx, "tcp", addr) if err != nil { d.logger.Error(err) } diff --git a/dialer/udp/dialer.go b/dialer/udp/dialer.go index 42a8261..cc57d1e 100644 --- a/dialer/udp/dialer.go +++ b/dialer/udp/dialer.go @@ -40,7 +40,7 @@ func (d *udpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp opt(&options) } - c, err := options.NetDialer.Dial(ctx, "udp", addr) + c, err := options.Dialer.Dial(ctx, "udp", addr) if err != nil { return nil, err } diff --git a/dialer/wg/dialer.go b/dialer/wg/dialer.go index 33ac93e..83e1335 100644 --- a/dialer/wg/dialer.go +++ b/dialer/wg/dialer.go @@ -40,7 +40,7 @@ func (d *wgDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOpt opt(&options) } - conn, err := options.NetDialer.Dial(ctx, "tcp", addr) + conn, err := options.Dialer.Dial(ctx, "tcp", addr) if err != nil { d.logger.Error(err) } diff --git a/dialer/ws/dialer.go b/dialer/ws/dialer.go index ccc5bc5..1d6e90f 100644 --- a/dialer/ws/dialer.go +++ b/dialer/ws/dialer.go @@ -57,7 +57,7 @@ func (d *wsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOpt opt(&options) } - conn, err := options.NetDialer.Dial(ctx, "tcp", addr) + conn, err := options.Dialer.Dial(ctx, "tcp", addr) if err != nil { d.options.Logger.Error(err) } diff --git a/go.mod b/go.mod index b50a1a3..fbd0250 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d github.com/gin-contrib/cors v1.6.0 github.com/gin-gonic/gin v1.9.1 - github.com/go-gost/core v0.0.0-20240704150322-30cc92870515 + github.com/go-gost/core v0.0.0-20240708142821-48d070d34568 github.com/go-gost/gosocks4 v0.0.1 github.com/go-gost/gosocks5 v0.4.2 github.com/go-gost/plugin v0.0.0-20240103125338-9c84e29cb81a @@ -20,7 +20,7 @@ require ( github.com/golang/snappy v0.0.4 github.com/google/uuid v1.6.0 github.com/gorilla/websocket v1.5.1 - github.com/miekg/dns v1.1.57 + github.com/miekg/dns v1.1.61 github.com/mitchellh/go-homedir v1.1.0 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pion/dtls/v2 v2.2.6 @@ -41,12 +41,12 @@ require ( github.com/xtaci/tcpraw v1.2.25 github.com/yl2chen/cidranger v1.0.2 github.com/zalando/go-keyring v0.2.4 - golang.org/x/crypto v0.24.0 - golang.org/x/net v0.26.0 - golang.org/x/sys v0.21.0 + golang.org/x/crypto v0.25.0 + golang.org/x/net v0.27.0 + golang.org/x/sys v0.22.0 golang.org/x/time v0.5.0 golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478 - google.golang.org/grpc v1.64.0 + google.golang.org/grpc v1.65.0 google.golang.org/protobuf v1.34.2 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v3 v3.0.1 @@ -57,7 +57,7 @@ require ( github.com/alessio/shellescape v1.4.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bytedance/sonic v1.11.2 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/chenzhuoyu/iasm v0.9.1 // indirect github.com/coreos/go-iptables v0.5.0 // indirect @@ -111,11 +111,11 @@ require ( go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.7.0 // indirect golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 // indirect - golang.org/x/mod v0.18.0 // indirect + golang.org/x/mod v0.19.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/text v0.16.0 // indirect - golang.org/x/tools v0.22.0 // indirect + golang.org/x/tools v0.23.0 // indirect golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect gopkg.in/ini.v1 v1.67.0 // indirect ) diff --git a/go.sum b/go.sum index a175c88..84f356f 100644 --- a/go.sum +++ b/go.sum @@ -15,8 +15,8 @@ github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ github.com/bytedance/sonic v1.11.2 h1:ywfwo0a/3j9HR8wsYGWsIWl2mvRsI950HyoxiBERw5A= github.com/bytedance/sonic v1.11.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= @@ -53,8 +53,8 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/go-gost/core v0.0.0-20240704150322-30cc92870515 h1:i/zcDZtz00hcmRosvJgXmgJsdc4bC32PAvt2+8MUOEg= -github.com/go-gost/core v0.0.0-20240704150322-30cc92870515/go.mod h1:QmVAZIXIYBsX44Vehwug5RFnG2K3/Hz/uu/Y4QVhAY0= +github.com/go-gost/core v0.0.0-20240708142821-48d070d34568 h1:OJ+FzmBMy9RnWdECxGw6FKHRmN346Q/iVmDUSCBIthk= +github.com/go-gost/core v0.0.0-20240708142821-48d070d34568/go.mod h1:WGI43jOka7FAsSAwi/fSMaqxdR+E339ycb4NBGlFr6A= github.com/go-gost/gosocks4 v0.0.1 h1:+k1sec8HlELuQV7rWftIkmy8UijzUt2I6t+iMPlGB2s= github.com/go-gost/gosocks4 v0.0.1/go.mod h1:3B6L47HbU/qugDg4JnoFPHgJXE43Inz8Bah1QaN9qCc= github.com/go-gost/gosocks5 v0.4.2 h1:IianxHTkACPqCwiOAT3MHoMdSUl+SEPSRu1ikawC1Pc= @@ -133,8 +133,8 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= -github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= +github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs= +github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -271,8 +271,8 @@ golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 h1:LoYXNGAShUG3m/ehNk4iFctuhGX/+R1ZpfJ4/ia80JM= golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= @@ -282,8 +282,8 @@ golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHl 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= -golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -295,8 +295,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 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= @@ -317,15 +317,15 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 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/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= 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.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -343,8 +343,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn 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.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= -golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= +golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= 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= @@ -356,14 +356,14 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl 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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 h1:9Xyg6I9IWQZhRVfCWjKK+l6kI0jHcPesVlMnT//aHNo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= -google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= 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= diff --git a/handler/dns/handler.go b/handler/dns/handler.go index e7c71fb..2cd4534 100644 --- a/handler/dns/handler.go +++ b/handler/dns/handler.go @@ -34,7 +34,6 @@ type dnsHandler struct { hop hop.Hop exchangers map[string]exchanger.Exchanger cache *resolver_util.Cache - router *chain.Router hostMapper hosts.HostMapper md metadata options handler.Options @@ -60,11 +59,7 @@ func (h *dnsHandler) Init(md md.Metadata) (err error) { h.cache = resolver_util.NewCache().WithLogger(log) - h.router = h.options.Router - if h.router == nil { - h.router = chain.NewRouter(chain.LoggerRouterOption(log)) - } - h.hostMapper = h.router.Options().HostMapper + h.hostMapper = h.options.Router.Options().HostMapper if h.hop == nil { var nodes []*chain.Node @@ -88,7 +83,7 @@ func (h *dnsHandler) Init(md md.Metadata) (err error) { } ex, err := exchanger.NewExchanger( addr, - exchanger.RouterOption(h.router), + exchanger.RouterOption(h.options.Router), exchanger.TimeoutOption(h.md.timeout), exchanger.LoggerOption(log), ) @@ -102,7 +97,7 @@ func (h *dnsHandler) Init(md md.Metadata) (err error) { if len(h.exchangers) == 0 { ex, err := exchanger.NewExchanger( defaultNameserver, - exchanger.RouterOption(h.router), + exchanger.RouterOption(h.options.Router), exchanger.TimeoutOption(h.md.timeout), exchanger.LoggerOption(log), ) diff --git a/handler/forward/local/handler.go b/handler/forward/local/handler.go index 0d8f9cb..957d55a 100644 --- a/handler/forward/local/handler.go +++ b/handler/forward/local/handler.go @@ -37,7 +37,6 @@ func init() { type forwardHandler struct { hop hop.Hop - router *chain.Router md metadata options handler.Options } @@ -58,11 +57,6 @@ func (h *forwardHandler) Init(md md.Metadata) (err error) { return } - h.router = h.options.Router - if h.router == nil { - h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger)) - } - return } @@ -157,7 +151,7 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand log.Debugf("%s >> %s", conn.RemoteAddr(), addr) - cc, err := h.router.Dial(ctx, network, addr) + cc, err := h.options.Router.Dial(ctx, network, addr) if err != nil { log.Error(err) // TODO: the router itself may be failed due to the failed node in the router, @@ -277,7 +271,7 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, remot } } - cc, err = h.router.Dial(ctx, "tcp", target.Addr) + cc, err = h.options.Router.Dial(ctx, "tcp", target.Addr) if err != nil { // TODO: the router itself may be failed due to the failed node in the router, // the dead marker may be a wrong operation. diff --git a/handler/forward/remote/handler.go b/handler/forward/remote/handler.go index c00da9f..dfd2ba2 100644 --- a/handler/forward/remote/handler.go +++ b/handler/forward/remote/handler.go @@ -38,7 +38,6 @@ func init() { type forwardHandler struct { hop hop.Hop - router *chain.Router md metadata options handler.Options } @@ -59,11 +58,6 @@ func (h *forwardHandler) Init(md mdata.Metadata) (err error) { return } - h.router = h.options.Router - if h.router == nil { - h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger)) - } - return } @@ -156,7 +150,7 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand log.Debugf("%s >> %s", conn.RemoteAddr(), target.Addr) - cc, err := h.router.Dial(ctx, network, target.Addr) + cc, err := h.options.Router.Dial(ctx, network, target.Addr) if err != nil { log.Error(err) // TODO: the router itself may be failed due to the failed node in the router, @@ -277,7 +271,7 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, remot } } - cc, err = h.router.Dial(ctx, "tcp", target.Addr) + cc, err = h.options.Router.Dial(ctx, "tcp", target.Addr) if err != nil { // TODO: the router itself may be failed due to the failed node in the router, // the dead marker may be a wrong operation. diff --git a/handler/http/handler.go b/handler/http/handler.go index 30efe32..392544f 100644 --- a/handler/http/handler.go +++ b/handler/http/handler.go @@ -18,7 +18,6 @@ import ( "time" "github.com/asaskevich/govalidator" - "github.com/go-gost/core/chain" "github.com/go-gost/core/handler" traffic "github.com/go-gost/core/limiter/traffic" "github.com/go-gost/core/logger" @@ -37,7 +36,6 @@ func init() { } type httpHandler struct { - router *chain.Router md metadata options handler.Options stats *stats_util.HandlerStats @@ -61,11 +59,6 @@ func (h *httpHandler) Init(md md.Metadata) error { return err } - h.router = h.options.Router - if h.router == nil { - h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger)) - } - ctx, cancel := context.WithCancel(context.Background()) h.cancel = cancel @@ -215,7 +208,7 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt ctx = ctxvalue.ContextWithHash(ctx, &ctxvalue.Hash{Source: addr}) } - cc, err := h.router.Dial(ctx, network, addr) + cc, err := h.options.Router.Dial(ctx, network, addr) if err != nil { resp.StatusCode = http.StatusServiceUnavailable diff --git a/handler/http/udp.go b/handler/http/udp.go index 52db1ca..5e6edf6 100644 --- a/handler/http/udp.go +++ b/handler/http/udp.go @@ -51,7 +51,7 @@ func (h *httpHandler) handleUDP(ctx context.Context, conn net.Conn, log logger.L } // obtain a udp connection - c, err := h.router.Dial(ctx, "udp", "") // UDP association + c, err := h.options.Router.Dial(ctx, "udp", "") // UDP association if err != nil { log.Error(err) return err diff --git a/handler/http2/handler.go b/handler/http2/handler.go index d95b029..5aa6e46 100644 --- a/handler/http2/handler.go +++ b/handler/http2/handler.go @@ -18,7 +18,6 @@ import ( "strings" "time" - "github.com/go-gost/core/chain" "github.com/go-gost/core/handler" "github.com/go-gost/core/limiter/traffic" "github.com/go-gost/core/logger" @@ -38,7 +37,6 @@ func init() { } type http2Handler struct { - router *chain.Router md metadata options handler.Options stats *stats_util.HandlerStats @@ -62,11 +60,6 @@ func (h *http2Handler) Init(md md.Metadata) error { return err } - h.router = h.options.Router - if h.router == nil { - h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger)) - } - ctx, cancel := context.WithCancel(context.Background()) h.cancel = cancel @@ -188,7 +181,7 @@ func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req ctx = ctxvalue.ContextWithHash(ctx, &ctxvalue.Hash{Source: addr}) } - cc, err := h.router.Dial(ctx, "tcp", addr) + cc, err := h.options.Router.Dial(ctx, "tcp", addr) if err != nil { log.Error(err) w.WriteHeader(http.StatusServiceUnavailable) diff --git a/handler/http3/handler.go b/handler/http3/handler.go index abee3b9..933b12a 100644 --- a/handler/http3/handler.go +++ b/handler/http3/handler.go @@ -24,7 +24,6 @@ func init() { type http3Handler struct { hop hop.Hop - router *chain.Router md metadata options handler.Options } @@ -45,11 +44,6 @@ func (h *http3Handler) Init(md md.Metadata) error { return err } - h.router = h.options.Router - if h.router == nil { - h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger)) - } - return nil } @@ -147,7 +141,7 @@ func (h *http3Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req TLSHandshakeTimeout: 10 * time.Second, ExpectContinueTimeout: 1 * time.Second, DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { - conn, err := h.router.Dial(ctx, network, target.Addr) + conn, err := h.options.Router.Dial(ctx, network, target.Addr) if err != nil { log.Error(err) // TODO: the router itself may be failed due to the failed node in the router, diff --git a/handler/redirect/tcp/handler.go b/handler/redirect/tcp/handler.go index a81a8a2..56d5e9b 100644 --- a/handler/redirect/tcp/handler.go +++ b/handler/redirect/tcp/handler.go @@ -15,7 +15,6 @@ import ( "time" "github.com/go-gost/core/bypass" - "github.com/go-gost/core/chain" "github.com/go-gost/core/handler" "github.com/go-gost/core/logger" md "github.com/go-gost/core/metadata" @@ -32,7 +31,6 @@ func init() { } type redirectHandler struct { - router *chain.Router md metadata options handler.Options } @@ -53,11 +51,6 @@ func (h *redirectHandler) Init(md md.Metadata) (err error) { return } - h.router = h.options.Router - if h.router == nil { - h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger)) - } - return } @@ -129,7 +122,7 @@ func (h *redirectHandler) Handle(ctx context.Context, conn net.Conn, opts ...han return nil } - cc, err := h.router.Dial(ctx, dstAddr.Network(), dstAddr.String()) + cc, err := h.options.Router.Dial(ctx, dstAddr.Network(), dstAddr.String()) if err != nil { log.Error(err) return err @@ -170,13 +163,13 @@ func (h *redirectHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, radd return nil } - cc, err := h.router.Dial(ctx, "tcp", host) + cc, err := h.options.Router.Dial(ctx, "tcp", host) if err != nil { log.Error(err) } if cc == nil { - cc, err = h.router.Dial(ctx, "tcp", dstAddr.String()) + cc, err = h.options.Router.Dial(ctx, "tcp", dstAddr.String()) if err != nil { log.Error(err) return err @@ -245,14 +238,14 @@ func (h *redirectHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, rad return nil } - cc, err = h.router.Dial(ctx, "tcp", host) + cc, err = h.options.Router.Dial(ctx, "tcp", host) if err != nil { log.Error(err) } } if cc == nil { - cc, err = h.router.Dial(ctx, "tcp", dstAddr.String()) + cc, err = h.options.Router.Dial(ctx, "tcp", dstAddr.String()) if err != nil { log.Error(err) return err diff --git a/handler/redirect/udp/handler.go b/handler/redirect/udp/handler.go index d340266..3fe6692 100644 --- a/handler/redirect/udp/handler.go +++ b/handler/redirect/udp/handler.go @@ -6,7 +6,6 @@ import ( "net" "time" - "github.com/go-gost/core/chain" "github.com/go-gost/core/handler" md "github.com/go-gost/core/metadata" netpkg "github.com/go-gost/x/internal/net" @@ -18,7 +17,6 @@ func init() { } type redirectHandler struct { - router *chain.Router md metadata options handler.Options } @@ -39,11 +37,6 @@ func (h *redirectHandler) Init(md md.Metadata) (err error) { return } - h.router = h.options.Router - if h.router == nil { - h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger)) - } - return } @@ -80,7 +73,7 @@ func (h *redirectHandler) Handle(ctx context.Context, conn net.Conn, opts ...han return nil } - cc, err := h.router.Dial(ctx, dstAddr.Network(), dstAddr.String()) + cc, err := h.options.Router.Dial(ctx, dstAddr.Network(), dstAddr.String()) if err != nil { log.Error(err) return err diff --git a/handler/relay/connect.go b/handler/relay/connect.go index 2fae3be..f956717 100644 --- a/handler/relay/connect.go +++ b/handler/relay/connect.go @@ -66,7 +66,7 @@ func (h *relayHandler) handleConnect(ctx context.Context, conn net.Conn, network case "serial": cc, err = serial.OpenPort(serial.ParseConfigFromAddr(address)) default: - cc, err = h.router.Dial(ctx, network, address) + cc, err = h.options.Router.Dial(ctx, network, address) } if err != nil { resp.Status = relay.StatusNetworkUnreachable diff --git a/handler/relay/forward.go b/handler/relay/forward.go index 8074c55..9f4f08f 100644 --- a/handler/relay/forward.go +++ b/handler/relay/forward.go @@ -38,7 +38,7 @@ func (h *relayHandler) handleForward(ctx context.Context, conn net.Conn, network log.Debugf("%s >> %s", conn.RemoteAddr(), target.Addr) - cc, err := h.router.Dial(ctx, network, target.Addr) + cc, err := h.options.Router.Dial(ctx, network, target.Addr) if err != nil { // TODO: the router itself may be failed due to the failed node in the router, // the dead marker may be a wrong operation. diff --git a/handler/relay/handler.go b/handler/relay/handler.go index 4b368ae..c86c02c 100644 --- a/handler/relay/handler.go +++ b/handler/relay/handler.go @@ -7,7 +7,6 @@ import ( "strconv" "time" - "github.com/go-gost/core/chain" "github.com/go-gost/core/handler" "github.com/go-gost/core/hop" md "github.com/go-gost/core/metadata" @@ -30,7 +29,6 @@ func init() { type relayHandler struct { hop hop.Hop - router *chain.Router md metadata options handler.Options stats *stats_util.HandlerStats @@ -54,11 +52,6 @@ func (h *relayHandler) Init(md md.Metadata) (err error) { return err } - h.router = h.options.Router - if h.router == nil { - h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger)) - } - ctx, cancel := context.WithCancel(context.Background()) h.cancel = cancel diff --git a/handler/serial/handler.go b/handler/serial/handler.go index 59853b4..313e430 100644 --- a/handler/serial/handler.go +++ b/handler/serial/handler.go @@ -25,7 +25,6 @@ func init() { type serialHandler struct { hop hop.Hop - router *chain.Router md metadata options handler.Options recorder recorder.RecorderObject @@ -47,11 +46,7 @@ func (h *serialHandler) Init(md md.Metadata) (err error) { return } - h.router = h.options.Router - if h.router == nil { - h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger)) - } - if opts := h.router.Options(); opts != nil { + if opts := h.options.Router.Options(); opts != nil { for _, ro := range opts.Recorders { if ro.Record == xrecorder.RecorderServiceHandlerSerial { h.recorder = ro @@ -97,7 +92,7 @@ func (h *serialHandler) Handle(ctx context.Context, conn net.Conn, opts ...handl return h.forwardSerial(ctx, conn, target, log) } - cc, err := h.router.Dial(ctx, "tcp", "@") + cc, err := h.options.Router.Dial(ctx, "tcp", "@") if err != nil { log.Error(err) return err @@ -121,8 +116,8 @@ func (h *serialHandler) forwardSerial(ctx context.Context, conn net.Conn, target cfg := serial.ParseConfigFromAddr(conn.LocalAddr().String()) cfg.Name = target.Addr - if opts := h.router.Options(); opts != nil && opts.Chain != nil { - port, err = h.router.Dial(ctx, "serial", serial.AddrFromConfig(cfg)) + if opts := h.options.Router.Options(); opts != nil && opts.Chain != nil { + port, err = h.options.Router.Dial(ctx, "serial", serial.AddrFromConfig(cfg)) } else { cfg.ReadTimeout = h.md.timeout port, err = serial.OpenPort(cfg) diff --git a/handler/sni/handler.go b/handler/sni/handler.go index 4cbb712..d70bf2d 100644 --- a/handler/sni/handler.go +++ b/handler/sni/handler.go @@ -16,7 +16,6 @@ import ( "time" "github.com/go-gost/core/bypass" - "github.com/go-gost/core/chain" "github.com/go-gost/core/handler" "github.com/go-gost/core/logger" md "github.com/go-gost/core/metadata" @@ -32,7 +31,6 @@ func init() { } type sniHandler struct { - router *chain.Router md metadata options handler.Options } @@ -55,11 +53,6 @@ func (h *sniHandler) Init(md md.Metadata) (err error) { return } - h.router = h.options.Router - if h.router == nil { - h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger)) - } - return nil } @@ -128,7 +121,7 @@ func (h *sniHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, raddr net ctx = ctxvalue.ContextWithHash(ctx, &ctxvalue.Hash{Source: host}) } - cc, err := h.router.Dial(ctx, "tcp", host) + cc, err := h.options.Router.Dial(ctx, "tcp", host) if err != nil { log.Error(err) return err @@ -196,7 +189,7 @@ func (h *sniHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, raddr ne ctx = ctxvalue.ContextWithHash(ctx, &ctxvalue.Hash{Source: host}) } - cc, err := h.router.Dial(ctx, "tcp", host) + cc, err := h.options.Router.Dial(ctx, "tcp", host) if err != nil { log.Error(err) return err diff --git a/handler/socks/v4/handler.go b/handler/socks/v4/handler.go index ee67332..2637b92 100644 --- a/handler/socks/v4/handler.go +++ b/handler/socks/v4/handler.go @@ -6,7 +6,6 @@ import ( "net" "time" - "github.com/go-gost/core/chain" "github.com/go-gost/core/handler" "github.com/go-gost/core/limiter/traffic" "github.com/go-gost/core/logger" @@ -32,7 +31,6 @@ func init() { } type socks4Handler struct { - router *chain.Router md metadata options handler.Options stats *stats_util.HandlerStats @@ -56,11 +54,6 @@ func (h *socks4Handler) Init(md md.Metadata) (err error) { return err } - h.router = h.options.Router - if h.router == nil { - h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger)) - } - ctx, cancel := context.WithCancel(context.Background()) h.cancel = cancel @@ -154,7 +147,7 @@ func (h *socks4Handler) handleConnect(ctx context.Context, conn net.Conn, req *g ctx = ctxvalue.ContextWithHash(ctx, &ctxvalue.Hash{Source: addr}) } - cc, err := h.router.Dial(ctx, "tcp", addr) + cc, err := h.options.Router.Dial(ctx, "tcp", addr) if err != nil { resp := gosocks4.NewReply(gosocks4.Failed, nil) log.Trace(resp) diff --git a/handler/socks/v5/connect.go b/handler/socks/v5/connect.go index 1ba0d03..6c2a266 100644 --- a/handler/socks/v5/connect.go +++ b/handler/socks/v5/connect.go @@ -35,7 +35,7 @@ func (h *socks5Handler) handleConnect(ctx context.Context, conn net.Conn, networ ctx = ctxvalue.ContextWithHash(ctx, &ctxvalue.Hash{Source: address}) } - cc, err := h.router.Dial(ctx, network, address) + cc, err := h.options.Router.Dial(ctx, network, address) if err != nil { resp := gosocks5.NewReply(gosocks5.NetUnreachable, nil) log.Trace(resp) diff --git a/handler/socks/v5/handler.go b/handler/socks/v5/handler.go index 3d14482..ece0b83 100644 --- a/handler/socks/v5/handler.go +++ b/handler/socks/v5/handler.go @@ -6,7 +6,6 @@ import ( "net" "time" - "github.com/go-gost/core/chain" "github.com/go-gost/core/handler" md "github.com/go-gost/core/metadata" "github.com/go-gost/gosocks5" @@ -27,7 +26,6 @@ func init() { type socks5Handler struct { selector gosocks5.Selector - router *chain.Router md metadata options handler.Options stats *stats_util.HandlerStats @@ -51,11 +49,6 @@ func (h *socks5Handler) Init(md md.Metadata) (err error) { return } - h.router = h.options.Router - if h.router == nil { - h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger)) - } - h.selector = &serverSelector{ Authenticator: h.options.Auther, TLSConfig: h.options.TLSConfig, diff --git a/handler/socks/v5/udp.go b/handler/socks/v5/udp.go index d4405f9..95f2669 100644 --- a/handler/socks/v5/udp.go +++ b/handler/socks/v5/udp.go @@ -59,7 +59,7 @@ func (h *socks5Handler) handleUDP(ctx context.Context, conn net.Conn, log logger log.Debugf("bind on %s OK", cc.LocalAddr()) // obtain a udp connection - c, err := h.router.Dial(ctx, "udp", "") // UDP association + c, err := h.options.Router.Dial(ctx, "udp", "") // UDP association if err != nil { log.Error(err) return err diff --git a/handler/socks/v5/udp_tun.go b/handler/socks/v5/udp_tun.go index 7b99c6b..b9d1ccb 100644 --- a/handler/socks/v5/udp_tun.go +++ b/handler/socks/v5/udp_tun.go @@ -37,7 +37,7 @@ func (h *socks5Handler) handleUDPTun(ctx context.Context, conn net.Conn, network } // obtain a udp connection - c, err := h.router.Dial(ctx, "udp", "") // UDP association + c, err := h.options.Router.Dial(ctx, "udp", "") // UDP association if err != nil { log.Error(err) return err diff --git a/handler/ss/handler.go b/handler/ss/handler.go index 3b58609..c6315cb 100644 --- a/handler/ss/handler.go +++ b/handler/ss/handler.go @@ -6,7 +6,6 @@ import ( "net" "time" - "github.com/go-gost/core/chain" "github.com/go-gost/core/handler" md "github.com/go-gost/core/metadata" "github.com/go-gost/gosocks5" @@ -23,7 +22,6 @@ func init() { type ssHandler struct { cipher core.Cipher - router *chain.Router md metadata options handler.Options } @@ -52,11 +50,6 @@ func (h *ssHandler) Init(md md.Metadata) (err error) { } } - h.router = h.options.Router - if h.router == nil { - h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger)) - } - return } @@ -111,7 +104,7 @@ func (h *ssHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.H ctx = ctxvalue.ContextWithHash(ctx, &ctxvalue.Hash{Source: addr.String()}) } - cc, err := h.router.Dial(ctx, "tcp", addr.String()) + cc, err := h.options.Router.Dial(ctx, "tcp", addr.String()) if err != nil { return err } diff --git a/handler/ss/udp/handler.go b/handler/ss/udp/handler.go index 4f31422..ef8cfed 100644 --- a/handler/ss/udp/handler.go +++ b/handler/ss/udp/handler.go @@ -6,7 +6,6 @@ import ( "net" "time" - "github.com/go-gost/core/chain" "github.com/go-gost/core/common/bufpool" "github.com/go-gost/core/handler" "github.com/go-gost/core/logger" @@ -23,7 +22,6 @@ func init() { type ssuHandler struct { cipher core.Cipher - router *chain.Router md metadata options handler.Options } @@ -53,11 +51,6 @@ func (h *ssuHandler) Init(md md.Metadata) (err error) { } } - h.router = h.options.Router - if h.router == nil { - h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger)) - } - return } @@ -97,7 +90,7 @@ func (h *ssuHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler. } // obtain a udp connection - c, err := h.router.Dial(ctx, "udp", "") // UDP association + c, err := h.options.Router.Dial(ctx, "udp", "") // UDP association if err != nil { log.Error(err) return err diff --git a/handler/sshd/handler.go b/handler/sshd/handler.go index e22fb98..884f629 100644 --- a/handler/sshd/handler.go +++ b/handler/sshd/handler.go @@ -9,7 +9,6 @@ import ( "strconv" "time" - "github.com/go-gost/core/chain" "github.com/go-gost/core/handler" "github.com/go-gost/core/logger" md "github.com/go-gost/core/metadata" @@ -29,7 +28,6 @@ func init() { } type forwardHandler struct { - router *chain.Router md metadata options handler.Options } @@ -50,11 +48,6 @@ func (h *forwardHandler) Init(md md.Metadata) (err error) { return } - h.router = h.options.Router - if h.router == nil { - h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger)) - } - return nil } @@ -97,7 +90,7 @@ func (h *forwardHandler) handleDirectForward(ctx context.Context, conn *sshd_uti return nil } - cc, err := h.router.Dial(ctx, "tcp", targetAddr) + cc, err := h.options.Router.Dial(ctx, "tcp", targetAddr) if err != nil { return err } diff --git a/handler/tap/handler.go b/handler/tap/handler.go index ccd1dc2..66be14e 100644 --- a/handler/tap/handler.go +++ b/handler/tap/handler.go @@ -33,7 +33,6 @@ type tapHandler struct { routes sync.Map exit chan struct{} cipher core.Cipher - router *chain.Router md metadata options handler.Options } @@ -64,11 +63,6 @@ func (h *tapHandler) Init(md md.Metadata) (err error) { } } - h.router = h.options.Router - if h.router == nil { - h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger)) - } - return } @@ -135,7 +129,7 @@ func (h *tapHandler) handleLoop(ctx context.Context, conn net.Conn, addr net.Add var pc net.PacketConn if addr != nil { - cc, err := h.router.Dial(ctx, addr.Network(), "") + cc, err := h.options.Router.Dial(ctx, addr.Network(), "") if err != nil { return err } diff --git a/handler/tun/client.go b/handler/tun/client.go index a02bb7c..926ab4c 100644 --- a/handler/tun/client.go +++ b/handler/tun/client.go @@ -35,7 +35,7 @@ func (h *tunHandler) handleClient(ctx context.Context, conn net.Conn, raddr stri for { err := func() error { - cc, err := h.router.Dial(ctx, "udp", raddr) + cc, err := h.options.Router.Dial(ctx, "udp", raddr) if err != nil { return err } diff --git a/handler/tun/handler.go b/handler/tun/handler.go index 2f87ffb..94aa93f 100644 --- a/handler/tun/handler.go +++ b/handler/tun/handler.go @@ -29,7 +29,6 @@ func init() { type tunHandler struct { hop hop.Hop routes sync.Map - router *chain.Router md metadata options handler.Options } @@ -50,11 +49,6 @@ func (h *tunHandler) Init(md md.Metadata) (err error) { return } - h.router = h.options.Router - if h.router == nil { - h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger)) - } - return } diff --git a/handler/unix/handler.go b/handler/unix/handler.go index d03cf56..f819493 100644 --- a/handler/unix/handler.go +++ b/handler/unix/handler.go @@ -8,8 +8,8 @@ import ( "time" "github.com/go-gost/core/chain" - "github.com/go-gost/core/hop" "github.com/go-gost/core/handler" + "github.com/go-gost/core/hop" "github.com/go-gost/core/logger" md "github.com/go-gost/core/metadata" xnet "github.com/go-gost/x/internal/net" @@ -22,7 +22,6 @@ func init() { type unixHandler struct { hop hop.Hop - router *chain.Router md metadata options handler.Options } @@ -43,11 +42,6 @@ func (h *unixHandler) Init(md md.Metadata) (err error) { return } - h.router = h.options.Router - if h.router == nil { - h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger)) - } - return } @@ -80,7 +74,7 @@ func (h *unixHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler return h.forwardUnix(ctx, conn, target, log) } - cc, err := h.router.Dial(ctx, "tcp", "@") + cc, err := h.options.Router.Dial(ctx, "tcp", "@") if err != nil { log.Error(err) return err @@ -101,8 +95,8 @@ func (h *unixHandler) forwardUnix(ctx context.Context, conn net.Conn, target *ch log.Debugf("%s >> %s", conn.LocalAddr(), target.Addr) var cc io.ReadWriteCloser - if opts := h.router.Options(); opts != nil && opts.Chain != nil { - cc, err = h.router.Dial(ctx, "unix", target.Addr) + if opts := h.options.Router.Options(); opts != nil && opts.Chain != nil { + cc, err = h.options.Router.Dial(ctx, "unix", target.Addr) } else { cc, err = (&net.Dialer{}).DialContext(ctx, "unix", target.Addr) } diff --git a/internal/net/addr.go b/internal/net/addr.go index 8b24a0b..9675946 100644 --- a/internal/net/addr.go +++ b/internal/net/addr.go @@ -7,6 +7,84 @@ import ( "strings" ) +func ParseInterfaceAddr(ifceName, network string) (ifce string, addr []net.Addr, err error) { + if ifceName == "" { + addr = append(addr, nil) + return + } + + ip := net.ParseIP(ifceName) + if ip == nil { + var ife *net.Interface + ife, err = net.InterfaceByName(ifceName) + if err != nil { + return + } + var addrs []net.Addr + addrs, err = ife.Addrs() + if err != nil { + return + } + if len(addrs) == 0 { + err = fmt.Errorf("addr not found for interface %s", ifceName) + return + } + ifce = ifceName + for _, addr_ := range addrs { + if ipNet, ok := addr_.(*net.IPNet); ok { + addr = append(addr, ipToAddr(ipNet.IP, network)) + } + } + } else { + ifce, err = findInterfaceByIP(ip) + if err != nil { + return + } + addr = []net.Addr{ipToAddr(ip, network)} + } + + return +} + +func ipToAddr(ip net.IP, network string) (addr net.Addr) { + port := 0 + switch network { + case "tcp", "tcp4", "tcp6": + addr = &net.TCPAddr{IP: ip, Port: port} + return + case "udp", "udp4", "udp6": + addr = &net.UDPAddr{IP: ip, Port: port} + return + default: + addr = &net.IPAddr{IP: ip} + return + } +} + +func findInterfaceByIP(ip net.IP) (string, error) { + ifces, err := net.Interfaces() + if err != nil { + return "", err + } + for _, ifce := range ifces { + addrs, _ := ifce.Addrs() + if len(addrs) == 0 { + continue + } + for _, addr := range addrs { + ipAddr, _ := addr.(*net.IPNet) + if ipAddr == nil { + continue + } + // logger.Default().Infof("%s-%s", ipAddr, ip) + if ipAddr.IP.Equal(ip) { + return ifce.Name, nil + } + } + } + return "", nil +} + // AddrPortRange is the network address with port range supported. // e.g. 192.168.1.1:0-65535 type AddrPortRange string diff --git a/internal/net/dialer/dialer.go b/internal/net/dialer/dialer.go new file mode 100644 index 0000000..f1b10a8 --- /dev/null +++ b/internal/net/dialer/dialer.go @@ -0,0 +1,176 @@ +package dialer + +import ( + "context" + "fmt" + "net" + "runtime" + "strings" + "syscall" + "time" + + "github.com/go-gost/core/logger" + xnet "github.com/go-gost/x/internal/net" + "github.com/vishvananda/netns" +) + +const ( + DefaultTimeout = 10 * time.Second +) + +var ( + DefaultNetDialer = &Dialer{} +) + +type Dialer struct { + Interface string + Netns string + Mark int + DialFunc func(ctx context.Context, network, addr string) (net.Conn, error) + Logger logger.Logger +} + +func (d *Dialer) Dial(ctx context.Context, network, addr string) (conn net.Conn, err error) { + if d == nil { + d = DefaultNetDialer + } + + log := d.Logger + if log == nil { + log = logger.Default() + } + + if d.Netns != "" { + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + originNs, err := netns.Get() + if err != nil { + return nil, fmt.Errorf("netns.Get(): %v", err) + } + defer netns.Set(originNs) + + var ns netns.NsHandle + if strings.HasPrefix(d.Netns, "/") { + ns, err = netns.GetFromPath(d.Netns) + } else { + ns, err = netns.GetFromName(d.Netns) + } + if err != nil { + return nil, fmt.Errorf("netns.Get(%s): %v", d.Netns, err) + } + defer ns.Close() + + if err := netns.Set(ns); err != nil { + return nil, fmt.Errorf("netns.Set(%s): %v", d.Netns, err) + } + } + + if d.DialFunc != nil { + return d.DialFunc(ctx, network, addr) + } + + switch network { + case "unix": + netd := net.Dialer{} + return netd.DialContext(ctx, network, addr) + default: + } + + ifces := strings.Split(d.Interface, ",") + for _, ifce := range ifces { + strict := strings.HasSuffix(ifce, "!") + ifce = strings.TrimSuffix(ifce, "!") + var ifceName string + var ifAddrs []net.Addr + ifceName, ifAddrs, err = xnet.ParseInterfaceAddr(ifce, network) + if err != nil && strict { + return + } + + for _, ifAddr := range ifAddrs { + conn, err = d.dialOnce(ctx, network, addr, ifceName, ifAddr, log) + if err == nil { + return + } + + log.Debugf("dial %s %v@%s failed: %s", network, ifAddr, ifceName, err) + + if strict && + !strings.Contains(err.Error(), "no suitable address found") && + !strings.Contains(err.Error(), "mismatched local address type") { + return + } + } + } + + return +} + +func (d *Dialer) dialOnce(ctx context.Context, network, addr, ifceName string, ifAddr net.Addr, log logger.Logger) (net.Conn, error) { + if ifceName != "" { + log.Debugf("interface: %s %v/%s", ifceName, ifAddr, network) + } + + switch network { + case "udp", "udp4", "udp6": + if addr == "" { + var laddr *net.UDPAddr + if ifAddr != nil { + laddr, _ = ifAddr.(*net.UDPAddr) + } + + c, err := net.ListenUDP(network, laddr) + if err != nil { + return nil, err + } + sc, err := c.SyscallConn() + if err != nil { + log.Error(err) + return nil, err + } + err = sc.Control(func(fd uintptr) { + if ifceName != "" { + if err := bindDevice(fd, ifceName); err != nil { + log.Warnf("bind device: %v", err) + } + } + if d.Mark != 0 { + if err := setMark(fd, d.Mark); err != nil { + log.Warnf("set mark: %v", err) + } + } + }) + if err != nil { + log.Error(err) + } + return c, nil + } + case "tcp", "tcp4", "tcp6": + default: + return nil, fmt.Errorf("dial: unsupported network %s", network) + } + netd := net.Dialer{ + LocalAddr: ifAddr, + Control: func(network, address string, c syscall.RawConn) error { + return c.Control(func(fd uintptr) { + if ifceName != "" { + if err := bindDevice(fd, ifceName); err != nil { + log.Warnf("bind device: %v", err) + } + } + if d.Mark != 0 { + if err := setMark(fd, d.Mark); err != nil { + log.Warnf("set mark: %v", err) + } + } + }) + }, + } + if d.Netns != "" { + // https://github.com/golang/go/issues/44922#issuecomment-796645858 + netd.FallbackDelay = -1 + } + + return netd.DialContext(ctx, network, addr) +} diff --git a/internal/net/dialer/dialer_linux.go b/internal/net/dialer/dialer_linux.go new file mode 100644 index 0000000..730af75 --- /dev/null +++ b/internal/net/dialer/dialer_linux.go @@ -0,0 +1,19 @@ +package dialer + +import ( + "golang.org/x/sys/unix" +) + +func bindDevice(fd uintptr, ifceName string) error { + if ifceName == "" { + return nil + } + return unix.BindToDevice(int(fd), ifceName) +} + +func setMark(fd uintptr, mark int) error { + if mark == 0 { + return nil + } + return unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_MARK, mark) +} diff --git a/internal/net/dialer/dialer_other.go b/internal/net/dialer/dialer_other.go new file mode 100644 index 0000000..fff35b4 --- /dev/null +++ b/internal/net/dialer/dialer_other.go @@ -0,0 +1,11 @@ +//go:build !linux + +package dialer + +func bindDevice(fd uintptr, ifceName string) error { + return nil +} + +func setMark(fd uintptr, mark int) error { + return nil +} diff --git a/internal/net/resovle.go b/internal/net/resovle.go new file mode 100644 index 0000000..fb5b8eb --- /dev/null +++ b/internal/net/resovle.go @@ -0,0 +1,44 @@ +package net + +import ( + "context" + "fmt" + "net" + + "github.com/go-gost/core/hosts" + "github.com/go-gost/core/logger" + "github.com/go-gost/core/resolver" +) + +func Resolve(ctx context.Context, network, addr string, r resolver.Resolver, hosts hosts.HostMapper, log logger.Logger) (string, error) { + if addr == "" { + return addr, nil + } + + host, port, _ := net.SplitHostPort(addr) + if host == "" { + return addr, nil + } + + if hosts != nil { + if ips, _ := hosts.Lookup(ctx, network, host); len(ips) > 0 { + log.Debugf("hit host mapper: %s -> %s", host, ips) + return net.JoinHostPort(ips[0].String(), port), nil + } + } + + if r != nil { + ips, err := r.Resolve(ctx, network, host) + if err != nil { + if err == resolver.ErrInvalid { + return addr, nil + } + log.Error(err) + } + if len(ips) == 0 { + return "", fmt.Errorf("resolver: domain %s does not exist", host) + } + return net.JoinHostPort(ips[0].String(), port), nil + } + return addr, nil +} diff --git a/internal/net/udp/listener.go b/internal/net/udp/listener.go new file mode 100644 index 0000000..b88b7ce --- /dev/null +++ b/internal/net/udp/listener.go @@ -0,0 +1,232 @@ +package udp + +import ( + "errors" + "net" + "sync" + "sync/atomic" + "time" + + "github.com/go-gost/core/common/bufpool" + "github.com/go-gost/core/logger" +) + +type ListenConfig struct { + Addr net.Addr + Backlog int + ReadQueueSize int + ReadBufferSize int + TTL time.Duration + KeepAlive bool + Logger logger.Logger +} +type listener struct { + conn net.PacketConn + cqueue chan net.Conn + connPool *connPool + // mux sync.Mutex + closed chan struct{} + errChan chan error + config *ListenConfig +} + +func NewListener(conn net.PacketConn, cfg *ListenConfig) net.Listener { + if cfg == nil { + cfg = &ListenConfig{} + } + + ln := &listener{ + conn: conn, + cqueue: make(chan net.Conn, cfg.Backlog), + closed: make(chan struct{}), + errChan: make(chan error, 1), + config: cfg, + } + if cfg.KeepAlive { + ln.connPool = newConnPool(cfg.TTL).WithLogger(cfg.Logger) + } + go ln.listenLoop() + + return ln +} + +func (ln *listener) Accept() (conn net.Conn, err error) { + select { + case conn = <-ln.cqueue: + return + case <-ln.closed: + return nil, net.ErrClosed + case err = <-ln.errChan: + if err == nil { + err = net.ErrClosed + } + return + } +} + +func (ln *listener) listenLoop() { + for { + select { + case <-ln.closed: + return + default: + } + + b := bufpool.Get(ln.config.ReadBufferSize) + + n, raddr, err := ln.conn.ReadFrom(b) + if err != nil { + ln.errChan <- err + close(ln.errChan) + return + } + + c := ln.getConn(raddr) + if c == nil { + bufpool.Put(b) + continue + } + + if err := c.WriteQueue(b[:n]); err != nil { + ln.config.Logger.Warn("data discarded: ", err) + } + } +} + +func (ln *listener) Addr() net.Addr { + if ln.config.Addr != nil { + return ln.config.Addr + } + return ln.conn.LocalAddr() +} + +func (ln *listener) Close() error { + select { + case <-ln.closed: + default: + close(ln.closed) + ln.conn.Close() + ln.connPool.Close() + } + + return nil +} + +func (ln *listener) getConn(raddr net.Addr) *conn { + // ln.mux.Lock() + // defer ln.mux.Unlock() + + c, ok := ln.connPool.Get(raddr.String()) + if ok { + return c + } + + c = newConn(ln.conn, ln.Addr(), raddr, ln.config.ReadQueueSize, ln.config.KeepAlive) + select { + case ln.cqueue <- c: + ln.connPool.Set(raddr.String(), c) + return c + default: + c.Close() + ln.config.Logger.Warnf("connection queue is full, client %s discarded", raddr) + return nil + } +} + +// conn is a server side connection for UDP client peer, it implements net.Conn and net.PacketConn. +type conn struct { + net.PacketConn + localAddr net.Addr + remoteAddr net.Addr + rc chan []byte // data receive queue + idle int32 // indicate the connection is idle + closed chan struct{} + closeMutex sync.Mutex + keepAlive bool +} + +func newConn(c net.PacketConn, laddr, remoteAddr net.Addr, queueSize int, keepAlive bool) *conn { + return &conn{ + PacketConn: c, + localAddr: laddr, + remoteAddr: remoteAddr, + rc: make(chan []byte, queueSize), + closed: make(chan struct{}), + keepAlive: keepAlive, + } +} + +func (c *conn) ReadFrom(b []byte) (n int, addr net.Addr, err error) { + select { + case bb := <-c.rc: + n = copy(b, bb) + c.SetIdle(false) + bufpool.Put(bb) + + case <-c.closed: + err = net.ErrClosed + return + } + + addr = c.remoteAddr + + return +} + +func (c *conn) Read(b []byte) (n int, err error) { + n, _, err = c.ReadFrom(b) + return +} + +func (c *conn) Write(b []byte) (n int, err error) { + n, err = c.WriteTo(b, c.remoteAddr) + if !c.keepAlive { + c.Close() + } + return +} + +func (c *conn) Close() error { + c.closeMutex.Lock() + defer c.closeMutex.Unlock() + + select { + case <-c.closed: + default: + close(c.closed) + } + return nil +} + +func (c *conn) LocalAddr() net.Addr { + return c.localAddr +} + +func (c *conn) RemoteAddr() net.Addr { + return c.remoteAddr +} + +func (c *conn) IsIdle() bool { + return atomic.LoadInt32(&c.idle) > 0 +} + +func (c *conn) SetIdle(idle bool) { + v := int32(0) + if idle { + v = 1 + } + atomic.StoreInt32(&c.idle, v) +} + +func (c *conn) WriteQueue(b []byte) error { + select { + case c.rc <- b: + return nil + + case <-c.closed: + return net.ErrClosed + + default: + return errors.New("recv queue is full") + } +} diff --git a/internal/net/udp/pool.go b/internal/net/udp/pool.go new file mode 100644 index 0000000..232d03d --- /dev/null +++ b/internal/net/udp/pool.go @@ -0,0 +1,115 @@ +package udp + +import ( + "sync" + "time" + + "github.com/go-gost/core/logger" +) + +type connPool struct { + m sync.Map + ttl time.Duration + closed chan struct{} + logger logger.Logger +} + +func newConnPool(ttl time.Duration) *connPool { + p := &connPool{ + ttl: ttl, + closed: make(chan struct{}), + } + go p.idleCheck() + return p +} + +func (p *connPool) WithLogger(logger logger.Logger) *connPool { + p.logger = logger + return p +} + +func (p *connPool) Get(key any) (c *conn, ok bool) { + if p == nil { + return + } + + v, ok := p.m.Load(key) + if ok { + c, ok = v.(*conn) + } + return +} + +func (p *connPool) Set(key any, c *conn) { + if p == nil { + return + } + + p.m.Store(key, c) +} + +func (p *connPool) Delete(key any) { + if p == nil { + return + } + p.m.Delete(key) +} + +func (p *connPool) Close() { + if p == nil { + return + } + + select { + case <-p.closed: + return + default: + } + + close(p.closed) + + p.m.Range(func(k, v any) bool { + if c, ok := v.(*conn); ok && c != nil { + c.Close() + } + return true + }) +} + +func (p *connPool) idleCheck() { + ticker := time.NewTicker(p.ttl) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + size := 0 + idles := 0 + p.m.Range(func(key, value any) bool { + c, ok := value.(*conn) + if !ok || c == nil { + p.Delete(key) + return true + } + size++ + + if c.IsIdle() { + idles++ + p.Delete(key) + c.Close() + return true + } + + c.SetIdle(true) + + return true + }) + + if idles > 0 { + p.logger.Debugf("connection pool: size=%d, idle=%d", size, idles) + } + case <-p.closed: + return + } + } +} diff --git a/listener/ftcp/listener.go b/listener/ftcp/listener.go index 957d793..58dc3c4 100644 --- a/listener/ftcp/listener.go +++ b/listener/ftcp/listener.go @@ -3,12 +3,12 @@ package ftcp import ( "net" - "github.com/go-gost/core/common/net/udp" "github.com/go-gost/core/listener" "github.com/go-gost/core/logger" md "github.com/go-gost/core/metadata" admission "github.com/go-gost/x/admission/wrapper" xnet "github.com/go-gost/x/internal/net" + "github.com/go-gost/x/internal/net/udp" limiter "github.com/go-gost/x/limiter/traffic/wrapper" metrics "github.com/go-gost/x/metrics/wrapper" stats "github.com/go-gost/x/observer/stats/wrapper" diff --git a/listener/redirect/udp/listener_linux.go b/listener/redirect/udp/listener_linux.go index ba2b779..3b0cfce 100644 --- a/listener/redirect/udp/listener_linux.go +++ b/listener/redirect/udp/listener_linux.go @@ -108,7 +108,7 @@ func (l *redirectListener) accept() (conn net.Conn, err error) { // Out-of-band data is also read in so that the original destination // address can be identified and parsed. func readFromUDP(conn *net.UDPConn, b []byte) (n int, remoteAddr *net.UDPAddr, dstAddr *net.UDPAddr, err error) { - oob := bufpool.Get(1024) + oob := bufpool.Get(8192) defer bufpool.Put(oob) n, oobn, _, remoteAddr, err := conn.ReadMsgUDP(b, oob) @@ -118,14 +118,14 @@ func readFromUDP(conn *net.UDPConn, b []byte) (n int, remoteAddr *net.UDPAddr, d msgs, err := unix.ParseSocketControlMessage(oob[:oobn]) if err != nil { - return 0, nil, nil, fmt.Errorf("parsing socket control message: %s", err) + return 0, nil, nil, fmt.Errorf("parsing socket control message: %v", err) } for _, msg := range msgs { if msg.Header.Level == unix.SOL_IP && msg.Header.Type == unix.IP_RECVORIGDSTADDR { originalDstRaw := &unix.RawSockaddrInet4{} if err = binary.Read(bytes.NewReader(msg.Data), binary.LittleEndian, originalDstRaw); err != nil { - return 0, nil, nil, fmt.Errorf("reading original destination address: %s", err) + return 0, nil, nil, fmt.Errorf("reading original destination address: %v", err) } switch originalDstRaw.Family { @@ -154,7 +154,7 @@ func readFromUDP(conn *net.UDPConn, b []byte) (n int, remoteAddr *net.UDPAddr, d } if dstAddr == nil { - return 0, nil, nil, fmt.Errorf("unable to obtain original destination: %s", err) + return 0, nil, nil, fmt.Errorf("unable to obtain original destination: %v", err) } return @@ -166,50 +166,50 @@ func readFromUDP(conn *net.UDPConn, b []byte) (n int, remoteAddr *net.UDPAddr, d func dialUDP(network string, laddr *net.UDPAddr, raddr *net.UDPAddr) (net.Conn, error) { remoteSocketAddress, err := udpAddrToSocketAddr(raddr) if err != nil { - return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("build destination socket address: %s", err)} + return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("build destination socket address: %v", err)} } localSocketAddress, err := udpAddrToSocketAddr(laddr) if err != nil { - return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("build local socket address: %s", err)} + return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("build local socket address: %v", err)} } fileDescriptor, err := unix.Socket(udpAddrFamily(network, laddr, raddr), unix.SOCK_DGRAM, 0) if err != nil { - return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("socket open: %s", err)} + return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("socket open: %v", err)} } if err = unix.SetsockoptInt(fileDescriptor, unix.SOL_IP, unix.IP_TRANSPARENT, 1); err != nil { unix.Close(fileDescriptor) - return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("set socket option: IP_TRANSPARENT: %s", err)} + return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("set socket option: IP_TRANSPARENT: %v", err)} } if err = unix.SetsockoptInt(fileDescriptor, unix.SOL_SOCKET, unix.SO_REUSEADDR, 1); err != nil { unix.Close(fileDescriptor) - return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("set socket option: SO_REUSEADDR: %s", err)} + return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("set socket option: SO_REUSEADDR: %v", err)} } if err = unix.SetsockoptInt(fileDescriptor, unix.SOL_SOCKET, unix.SO_REUSEPORT, 1); err != nil { unix.Close(fileDescriptor) - return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("set socket option: SO_REUSEPORT: %s", err)} + return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("set socket option: SO_REUSEPORT: %v", err)} } if err = unix.Bind(fileDescriptor, localSocketAddress); err != nil { unix.Close(fileDescriptor) - return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("socket bind %v: %s", laddr, err)} + return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("socket bind %v: %v", laddr, err)} } if err = unix.Connect(fileDescriptor, remoteSocketAddress); err != nil { unix.Close(fileDescriptor) - return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("socket connect: %s", err)} + return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("socket connect: %v", err)} } - fdFile := os.NewFile(uintptr(fileDescriptor), fmt.Sprintf("net-udp-dial-%s", raddr.String())) + fdFile := os.NewFile(uintptr(fileDescriptor), fmt.Sprintf("net-udp-dial-%v", raddr.String())) defer fdFile.Close() remoteConn, err := net.FileConn(fdFile) if err != nil { unix.Close(fileDescriptor) - return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("convert file descriptor to connection: %s", err)} + return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("convert file descriptor to connection: %v", err)} } return remoteConn, nil diff --git a/listener/rtcp/listener.go b/listener/rtcp/listener.go index d553089..564c267 100644 --- a/listener/rtcp/listener.go +++ b/listener/rtcp/listener.go @@ -25,7 +25,6 @@ func init() { type rtcpListener struct { laddr net.Addr ln net.Listener - router *chain.Router logger logger.Logger closed chan struct{} options listener.Options @@ -60,11 +59,6 @@ func (l *rtcpListener) Init(md md.Metadata) (err error) { l.laddr = &bindAddr{addr: l.options.Addr} } - l.router = l.options.Router - if l.router == nil { - l.router = chain.NewRouter(chain.LoggerRouterOption(l.logger)) - } - return } @@ -77,7 +71,7 @@ func (l *rtcpListener) Accept() (conn net.Conn, err error) { ln := l.getListener() if ln == nil { - ln, err = l.router.Bind( + ln, err = l.options.Router.Bind( context.Background(), "tcp", l.laddr.String(), chain.MuxBindOption(true), ) diff --git a/listener/rudp/listener.go b/listener/rudp/listener.go index 8d9e310..b98bb64 100644 --- a/listener/rudp/listener.go +++ b/listener/rudp/listener.go @@ -24,7 +24,6 @@ func init() { type rudpListener struct { laddr net.Addr ln net.Listener - router *chain.Router closed chan struct{} logger logger.Logger md metadata @@ -60,11 +59,6 @@ func (l *rudpListener) Init(md md.Metadata) (err error) { l.laddr = &bindAddr{addr: l.options.Addr} } - l.router = l.options.Router - if l.router == nil { - l.router = chain.NewRouter(chain.LoggerRouterOption(l.logger)) - } - return } @@ -77,7 +71,7 @@ func (l *rudpListener) Accept() (conn net.Conn, err error) { ln := l.getListener() if ln == nil { - ln, err = l.router.Bind( + ln, err = l.options.Router.Bind( context.Background(), "udp", l.laddr.String(), chain.BacklogBindOption(l.md.backlog), chain.UDPConnTTLBindOption(l.md.ttl), diff --git a/listener/udp/listener.go b/listener/udp/listener.go index d437a3c..d74c7a1 100644 --- a/listener/udp/listener.go +++ b/listener/udp/listener.go @@ -3,12 +3,12 @@ package udp import ( "net" - "github.com/go-gost/core/common/net/udp" "github.com/go-gost/core/listener" "github.com/go-gost/core/logger" md "github.com/go-gost/core/metadata" admission "github.com/go-gost/x/admission/wrapper" xnet "github.com/go-gost/x/internal/net" + "github.com/go-gost/x/internal/net/udp" limiter "github.com/go-gost/x/limiter/traffic/wrapper" metrics "github.com/go-gost/x/metrics/wrapper" stats "github.com/go-gost/x/observer/stats/wrapper" diff --git a/resolver/exchanger/exchanger.go b/resolver/exchanger/exchanger.go index 105fc7c..ce9ccf0 100644 --- a/resolver/exchanger/exchanger.go +++ b/resolver/exchanger/exchanger.go @@ -14,11 +14,12 @@ import ( "github.com/go-gost/core/chain" "github.com/go-gost/core/logger" + xchain "github.com/go-gost/x/chain" "github.com/miekg/dns" ) type Options struct { - router *chain.Router + router chain.Router tlsConfig *tls.Config timeout time.Duration logger logger.Logger @@ -28,7 +29,7 @@ type Options struct { type Option func(opts *Options) // RouterOption sets the router for Exchanger. -func RouterOption(router *chain.Router) Option { +func RouterOption(router chain.Router) Option { return func(opts *Options) { opts.router = router } @@ -65,7 +66,7 @@ type exchanger struct { network string addr string rawAddr string - router *chain.Router + router chain.Router client *http.Client options Options } @@ -102,7 +103,7 @@ func NewExchanger(addr string, opts ...Option) (Exchanger, error) { ex.addr = net.JoinHostPort(ex.addr, "53") } if ex.router == nil { - ex.router = chain.NewRouter(chain.LoggerRouterOption(options.logger)) + ex.router = xchain.NewRouter(chain.LoggerRouterOption(options.logger)) } switch ex.network { diff --git a/resolver/resolver.go b/resolver/resolver.go index 6319149..5e2b658 100644 --- a/resolver/resolver.go +++ b/resolver/resolver.go @@ -9,6 +9,7 @@ import ( "github.com/go-gost/core/chain" "github.com/go-gost/core/logger" "github.com/go-gost/core/resolver" + xchain "github.com/go-gost/x/chain" resolver_util "github.com/go-gost/x/internal/util/resolver" "github.com/go-gost/x/resolver/exchanger" "github.com/miekg/dns" @@ -67,7 +68,7 @@ func NewResolver(nameservers []NameServer, opts ...Option) (resolver.Resolver, e ex, err := exchanger.NewExchanger( addr, exchanger.RouterOption( - chain.NewRouter( + xchain.NewRouter( chain.ChainRouterOption(server.Chain), chain.LoggerRouterOption(options.logger), ), @@ -165,7 +166,7 @@ func (r *localResolver) resolveAsync(ctx context.Context, server *NameServer, ho return } -func (r *localResolver) lookupCache(ctx context.Context, server *NameServer, host string) (ips []net.IP, ttl time.Duration, ok bool) { +func (r *localResolver) lookupCache(_ context.Context, server *NameServer, host string) (ips []net.IP, ttl time.Duration, ok bool) { lookup := func(t uint16, host string) (ips []net.IP, ttl time.Duration, ok bool) { mq := dns.Msg{} mq.SetQuestion(dns.Fqdn(host), t) From 59b99a5b446f9207051a6e538565642119c1b0f5 Mon Sep 17 00:00:00 2001 From: ginuerzh Date: Mon, 8 Jul 2024 22:44:29 +0800 Subject: [PATCH 5/9] v0.1.0 --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index fbd0250..d65431a 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d github.com/gin-contrib/cors v1.6.0 github.com/gin-gonic/gin v1.9.1 - github.com/go-gost/core v0.0.0-20240708142821-48d070d34568 + github.com/go-gost/core v0.1.0 github.com/go-gost/gosocks4 v0.0.1 github.com/go-gost/gosocks5 v0.4.2 github.com/go-gost/plugin v0.0.0-20240103125338-9c84e29cb81a diff --git a/go.sum b/go.sum index 84f356f..c61997e 100644 --- a/go.sum +++ b/go.sum @@ -53,8 +53,8 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/go-gost/core v0.0.0-20240708142821-48d070d34568 h1:OJ+FzmBMy9RnWdECxGw6FKHRmN346Q/iVmDUSCBIthk= -github.com/go-gost/core v0.0.0-20240708142821-48d070d34568/go.mod h1:WGI43jOka7FAsSAwi/fSMaqxdR+E339ycb4NBGlFr6A= +github.com/go-gost/core v0.1.0 h1:LJJc8PIlRflE8ZIpxls+wYX1e8OGB0nUKJYh8HevM4U= +github.com/go-gost/core v0.1.0/go.mod h1:WGI43jOka7FAsSAwi/fSMaqxdR+E339ycb4NBGlFr6A= github.com/go-gost/gosocks4 v0.0.1 h1:+k1sec8HlELuQV7rWftIkmy8UijzUt2I6t+iMPlGB2s= github.com/go-gost/gosocks4 v0.0.1/go.mod h1:3B6L47HbU/qugDg4JnoFPHgJXE43Inz8Bah1QaN9qCc= github.com/go-gost/gosocks5 v0.4.2 h1:IianxHTkACPqCwiOAT3MHoMdSUl+SEPSRu1ikawC1Pc= From f2e32080e4be73756a7595026356b7b02b9ec48d Mon Sep 17 00:00:00 2001 From: ginuerzh Date: Wed, 10 Jul 2024 22:16:47 +0800 Subject: [PATCH 6/9] fix cutHost with auth info --- config/cmd/cmd.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/config/cmd/cmd.go b/config/cmd/cmd.go index 1ae66be..1280752 100644 --- a/config/cmd/cmd.go +++ b/config/cmd/cmd.go @@ -330,7 +330,12 @@ func cutHost(s string) (host, remain string) { } else { end += start } + // auth info + if n = strings.LastIndexByte(s[start:end], '@'); n >= 0 { + start += (n + 1) + } host = s[start:end] + remain = s[:start] + s[end:] return From 4a4c64cc66525ef7c6b207de767f168e376615c0 Mon Sep 17 00:00:00 2001 From: ginuerzh Date: Wed, 10 Jul 2024 22:57:49 +0800 Subject: [PATCH 7/9] fix host parsing --- handler/forward/local/handler.go | 4 +- handler/forward/remote/handler.go | 2 +- handler/http/handler.go | 2 +- handler/http2/handler.go | 2 +- handler/http3/handler.go | 3 +- handler/redirect/tcp/handler.go | 6 +- handler/sni/handler.go | 5 +- handler/tunnel/entrypoint.go | 2 +- listener/quic/listener.go | 3 +- listener/redirect/udp/conn.go | 10 +-- listener/redirect/udp/listener_linux.go | 93 +++++++++++++++---------- 11 files changed, 76 insertions(+), 56 deletions(-) diff --git a/handler/forward/local/handler.go b/handler/forward/local/handler.go index 957d55a..f076d2b 100644 --- a/handler/forward/local/handler.go +++ b/handler/forward/local/handler.go @@ -110,7 +110,7 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand } if _, _, err := net.SplitHostPort(host); err != nil { - host = net.JoinHostPort(host, "0") + host = net.JoinHostPort(strings.Trim(host, "[]"), "0") } var target *chain.Node @@ -202,7 +202,7 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, remot host := req.Host if _, _, err := net.SplitHostPort(host); err != nil { - host = net.JoinHostPort(host, "80") + host = net.JoinHostPort(strings.Trim(host, "[]"), "80") } if bp := h.options.Bypass; bp != nil && bp.Contains(ctx, "tcp", host, bypass.WithPathOption(req.RequestURI)) { log.Debugf("bypass: %s %s", host, req.RequestURI) diff --git a/handler/forward/remote/handler.go b/handler/forward/remote/handler.go index dfd2ba2..82fad11 100644 --- a/handler/forward/remote/handler.go +++ b/handler/forward/remote/handler.go @@ -203,7 +203,7 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, remot host := req.Host if _, _, err := net.SplitHostPort(host); err != nil { - host = net.JoinHostPort(host, "80") + host = net.JoinHostPort(strings.Trim(host, "[]"), "80") } if bp := h.options.Bypass; bp != nil && bp.Contains(ctx, "tcp", host, bypass.WithPathOption(req.RequestURI)) { log.Debugf("bypass: %s %s", host, req.RequestURI) diff --git a/handler/http/handler.go b/handler/http/handler.go index 392544f..0829274 100644 --- a/handler/http/handler.go +++ b/handler/http/handler.go @@ -136,7 +136,7 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt addr := req.Host if _, port, _ := net.SplitHostPort(addr); port == "" { - addr = net.JoinHostPort(addr, "80") + addr = net.JoinHostPort(strings.Trim(addr, "[]"), "80") } fields := map[string]any{ diff --git a/handler/http2/handler.go b/handler/http2/handler.go index 5aa6e46..def7ad1 100644 --- a/handler/http2/handler.go +++ b/handler/http2/handler.go @@ -132,7 +132,7 @@ func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req addr := req.Host if _, port, _ := net.SplitHostPort(addr); port == "" { - addr = net.JoinHostPort(addr, "80") + addr = net.JoinHostPort(strings.Trim(addr, "[]"), "80") } fields := map[string]any{ diff --git a/handler/http3/handler.go b/handler/http3/handler.go index 933b12a..8be8c93 100644 --- a/handler/http3/handler.go +++ b/handler/http3/handler.go @@ -7,6 +7,7 @@ import ( "net" "net/http" "net/http/httputil" + "strings" "time" "github.com/go-gost/core/chain" @@ -88,7 +89,7 @@ func (h *http3Handler) Handle(ctx context.Context, conn net.Conn, opts ...handle func (h *http3Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req *http.Request, log logger.Logger) error { addr := req.Host if _, port, _ := net.SplitHostPort(addr); port == "" { - addr = net.JoinHostPort(addr, "80") + addr = net.JoinHostPort(strings.Trim(addr, "[]"), "80") } if log.IsLevelEnabled(logger.TraceLevel) { diff --git a/handler/redirect/tcp/handler.go b/handler/redirect/tcp/handler.go index 56d5e9b..bd17b64 100644 --- a/handler/redirect/tcp/handler.go +++ b/handler/redirect/tcp/handler.go @@ -152,7 +152,7 @@ func (h *redirectHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, radd host := req.Host if _, _, err := net.SplitHostPort(host); err != nil { - host = net.JoinHostPort(host, "80") + host = net.JoinHostPort(strings.Trim(host, "[]"), "80") } log = log.WithFields(map[string]any{ "host": host, @@ -227,7 +227,7 @@ func (h *redirectHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, rad if port == "" { port = "443" } - host = net.JoinHostPort(host, port) + host = net.JoinHostPort(strings.Trim(host, "[]"), port) } log = log.WithFields(map[string]any{ "host": host, @@ -263,7 +263,7 @@ func (h *redirectHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, rad return nil } -func (h *redirectHandler) getServerName(ctx context.Context, r io.Reader) (host string, err error) { +func (h *redirectHandler) getServerName(_ context.Context, r io.Reader) (host string, err error) { record, err := dissector.ReadRecord(r) if err != nil { return diff --git a/handler/sni/handler.go b/handler/sni/handler.go index d70bf2d..8875ec4 100644 --- a/handler/sni/handler.go +++ b/handler/sni/handler.go @@ -13,6 +13,7 @@ import ( "net" "net/http" "net/http/httputil" + "strings" "time" "github.com/go-gost/core/bypass" @@ -105,7 +106,7 @@ func (h *sniHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, raddr net host := req.Host if _, _, err := net.SplitHostPort(host); err != nil { - host = net.JoinHostPort(host, "80") + host = net.JoinHostPort(strings.Trim(host, "[]"), "80") } log = log.WithFields(map[string]any{ "host": host, @@ -171,7 +172,7 @@ func (h *sniHandler) handleHTTPS(ctx context.Context, rw io.ReadWriter, raddr ne } if _, _, err := net.SplitHostPort(host); err != nil { - host = net.JoinHostPort(host, "443") + host = net.JoinHostPort(strings.Trim(host, "[]"), "443") } log = log.WithFields(map[string]any{ diff --git a/handler/tunnel/entrypoint.go b/handler/tunnel/entrypoint.go index a8f3126..b582431 100644 --- a/handler/tunnel/entrypoint.go +++ b/handler/tunnel/entrypoint.go @@ -132,7 +132,7 @@ func (ep *entrypoint) handle(ctx context.Context, conn net.Conn) error { host := req.Host if h, _, _ := net.SplitHostPort(host); h == "" { - host = net.JoinHostPort(host, "80") + host = net.JoinHostPort(strings.Trim(host, "[]"), "80") } if node == ep.node { diff --git a/listener/quic/listener.go b/listener/quic/listener.go index 1c8a070..dff8bfa 100644 --- a/listener/quic/listener.go +++ b/listener/quic/listener.go @@ -3,6 +3,7 @@ package quic import ( "context" "net" + "strings" "github.com/go-gost/core/listener" "github.com/go-gost/core/logger" @@ -48,7 +49,7 @@ func (l *quicListener) Init(md md.Metadata) (err error) { addr := l.options.Addr if _, _, err := net.SplitHostPort(addr); err != nil { - addr = net.JoinHostPort(addr, "0") + addr = net.JoinHostPort(strings.Trim(addr, "[]"), "0") } network := "udp" diff --git a/listener/redirect/udp/conn.go b/listener/redirect/udp/conn.go index cae9dad..1111bad 100644 --- a/listener/redirect/udp/conn.go +++ b/listener/redirect/udp/conn.go @@ -16,16 +16,16 @@ type redirConn struct { } func (c *redirConn) Read(b []byte) (n int, err error) { - if c.ttl > 0 { - c.SetReadDeadline(time.Now().Add(c.ttl)) - defer c.SetReadDeadline(time.Time{}) - } - c.once.Do(func() { n = copy(b, c.buf) bufpool.Put(c.buf) }) + if c.ttl > 0 { + c.SetReadDeadline(time.Now().Add(c.ttl)) + defer c.SetReadDeadline(time.Time{}) + } + if n == 0 { n, err = c.Conn.Read(b) } diff --git a/listener/redirect/udp/listener_linux.go b/listener/redirect/udp/listener_linux.go index 3b0cfce..db2504a 100644 --- a/listener/redirect/udp/listener_linux.go +++ b/listener/redirect/udp/listener_linux.go @@ -19,15 +19,30 @@ import ( "golang.org/x/sys/unix" ) +// https://github.com/KatelynHaworth/go-tproxy func (l *redirectListener) listenUDP(addr string) (*net.UDPConn, error) { + laddr, err := net.ResolveUDPAddr("udp", addr) + if err != nil { + return nil, err + } + lc := net.ListenConfig{ Control: func(network, address string, c syscall.RawConn) error { return c.Control(func(fd uintptr) { - if err := unix.SetsockoptInt(int(fd), unix.SOL_IP, unix.IP_TRANSPARENT, 1); err != nil { - l.logger.Errorf("SetsockoptInt(SOL_IP, IP_TRANSPARENT, 1): %v", err) - } - if err := unix.SetsockoptInt(int(fd), unix.SOL_IP, unix.IP_RECVORIGDSTADDR, 1); err != nil { - l.logger.Errorf("SetsockoptInt(SOL_IP, IP_RECVORIGDSTADDR, 1): %v", err) + if laddr.IP.To4() != nil { + if err := unix.SetsockoptInt(int(fd), unix.SOL_IP, unix.IP_TRANSPARENT, 1); err != nil { + l.logger.Errorf("SetsockoptInt(SOL_IP, IP_TRANSPARENT, 1): %v", err) + } + if err := unix.SetsockoptInt(int(fd), unix.SOL_IP, unix.IP_RECVORIGDSTADDR, 1); err != nil { + l.logger.Errorf("SetsockoptInt(SOL_IP, IP_RECVORIGDSTADDR, 1): %v", err) + } + } else { + if err := unix.SetsockoptInt(int(fd), unix.SOL_IPV6, unix.IPV6_TRANSPARENT, 1); err != nil { + l.logger.Errorf("SetsockoptInt(SOL_IPV6, IPV6_TRANSPARENT, 1): %v", err) + } + if err := unix.SetsockoptInt(int(fd), unix.SOL_IPV6, unix.IPV6_RECVORIGDSTADDR, 1); err != nil { + l.logger.Errorf("SetsockoptInt(SOL_IPV6, IPV6_RECVORIGDSTADDR, 1): %v", err) + } } }) }, @@ -83,11 +98,7 @@ func (l *redirectListener) accept() (conn net.Conn, err error) { } } - network := "udp" - if xnet.IsIPv4(l.options.Addr) { - network = "udp4" - } - c, err := dialUDP(network, dstAddr, raddr) + c, err := dialUDP("udp", dstAddr, raddr) if err != nil { l.logger.Error(err) return @@ -128,28 +139,24 @@ func readFromUDP(conn *net.UDPConn, b []byte) (n int, remoteAddr *net.UDPAddr, d return 0, nil, nil, fmt.Errorf("reading original destination address: %v", err) } - switch originalDstRaw.Family { - case unix.AF_INET: - pp := (*unix.RawSockaddrInet4)(unsafe.Pointer(originalDstRaw)) - p := (*[2]byte)(unsafe.Pointer(&pp.Port)) - dstAddr = &net.UDPAddr{ - IP: net.IPv4(pp.Addr[0], pp.Addr[1], pp.Addr[2], pp.Addr[3]), - Port: int(p[0])<<8 + int(p[1]), - } - - case unix.AF_INET6: - pp := (*unix.RawSockaddrInet6)(unsafe.Pointer(originalDstRaw)) - p := (*[2]byte)(unsafe.Pointer(&pp.Port)) - dstAddr = &net.UDPAddr{ - IP: net.IP(pp.Addr[:]), - Port: int(p[0])<<8 + int(p[1]), - Zone: strconv.Itoa(int(pp.Scope_id)), - } - - default: - return 0, nil, nil, fmt.Errorf("original destination is an unsupported network family") + pp := (*unix.RawSockaddrInet4)(unsafe.Pointer(originalDstRaw)) + p := (*[2]byte)(unsafe.Pointer(&pp.Port)) + dstAddr = &net.UDPAddr{ + IP: net.IPv4(pp.Addr[0], pp.Addr[1], pp.Addr[2], pp.Addr[3]), + Port: int(p[0])<<8 + int(p[1]), + } + } else if msg.Header.Level == unix.SOL_IPV6 && msg.Header.Type == unix.IPV6_RECVORIGDSTADDR { + inet6 := &unix.RawSockaddrInet6{} + if err = binary.Read(bytes.NewReader(msg.Data), binary.LittleEndian, inet6); err != nil { + return 0, nil, nil, fmt.Errorf("reading original destination address: %v", err) + } + + p := (*[2]byte)(unsafe.Pointer(&inet6.Port)) + dstAddr = &net.UDPAddr{ + IP: net.IP(inet6.Addr[:]), + Port: int(p[0])<<8 + int(p[1]), + Zone: strconv.Itoa(int(inet6.Scope_id)), } - break } } @@ -179,9 +186,16 @@ func dialUDP(network string, laddr *net.UDPAddr, raddr *net.UDPAddr) (net.Conn, return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("socket open: %v", err)} } - if err = unix.SetsockoptInt(fileDescriptor, unix.SOL_IP, unix.IP_TRANSPARENT, 1); err != nil { - unix.Close(fileDescriptor) - return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("set socket option: IP_TRANSPARENT: %v", err)} + if laddr.IP.To4() != nil { + if err = unix.SetsockoptInt(fileDescriptor, unix.SOL_IP, unix.IP_TRANSPARENT, 1); err != nil { + unix.Close(fileDescriptor) + return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("set socket option: IP_TRANSPARENT: %v", err)} + } + } else { + if err = unix.SetsockoptInt(fileDescriptor, unix.SOL_IPV6, unix.IPV6_TRANSPARENT, 1); err != nil { + unix.Close(fileDescriptor) + return nil, &net.OpError{Op: "dial", Err: fmt.Errorf("set socket option: IPV6_TRANSPARENT: %v", err)} + } } if err = unix.SetsockoptInt(fileDescriptor, unix.SOL_SOCKET, unix.SO_REUSEADDR, 1); err != nil { @@ -230,9 +244,12 @@ func udpAddrToSocketAddr(addr *net.UDPAddr) (unix.Sockaddr, error) { ip := [16]byte{} copy(ip[:], addr.IP.To16()) - zoneID, err := strconv.ParseUint(addr.Zone, 10, 32) - if err != nil { - return nil, err + var zoneID uint64 + if addr.Zone != "" { + zoneID, _ = strconv.ParseUint(addr.Zone, 10, 32) + if zoneID == 0 { + zoneID = 2 + } } return &unix.SockaddrInet6{Addr: ip, Port: addr.Port, ZoneId: uint32(zoneID)}, nil @@ -251,7 +268,7 @@ func udpAddrFamily(net string, laddr, raddr *net.UDPAddr) int { } if (laddr == nil || laddr.IP.To4() != nil) && - (raddr == nil || laddr.IP.To4() != nil) { + (raddr == nil || raddr.IP.To4() != nil) { return unix.AF_INET } return unix.AF_INET6 From 2a75be91b080efe56bde38d72e499e8f607c0f85 Mon Sep 17 00:00:00 2001 From: ginuerzh Date: Thu, 11 Jul 2024 22:27:02 +0800 Subject: [PATCH 8/9] fix ipv6 for udp tproxy --- listener/redirect/udp/listener_linux.go | 44 +++++++++++++------------ 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/listener/redirect/udp/listener_linux.go b/listener/redirect/udp/listener_linux.go index db2504a..b720aeb 100644 --- a/listener/redirect/udp/listener_linux.go +++ b/listener/redirect/udp/listener_linux.go @@ -21,28 +21,27 @@ import ( // https://github.com/KatelynHaworth/go-tproxy func (l *redirectListener) listenUDP(addr string) (*net.UDPConn, error) { - laddr, err := net.ResolveUDPAddr("udp", addr) - if err != nil { - return nil, err - } + /* + laddr, err := net.ResolveUDPAddr("udp", addr) + if err != nil { + return nil, err + } + */ lc := net.ListenConfig{ Control: func(network, address string, c syscall.RawConn) error { return c.Control(func(fd uintptr) { - if laddr.IP.To4() != nil { - if err := unix.SetsockoptInt(int(fd), unix.SOL_IP, unix.IP_TRANSPARENT, 1); err != nil { - l.logger.Errorf("SetsockoptInt(SOL_IP, IP_TRANSPARENT, 1): %v", err) - } - if err := unix.SetsockoptInt(int(fd), unix.SOL_IP, unix.IP_RECVORIGDSTADDR, 1); err != nil { - l.logger.Errorf("SetsockoptInt(SOL_IP, IP_RECVORIGDSTADDR, 1): %v", err) - } - } else { - if err := unix.SetsockoptInt(int(fd), unix.SOL_IPV6, unix.IPV6_TRANSPARENT, 1); err != nil { - l.logger.Errorf("SetsockoptInt(SOL_IPV6, IPV6_TRANSPARENT, 1): %v", err) - } - if err := unix.SetsockoptInt(int(fd), unix.SOL_IPV6, unix.IPV6_RECVORIGDSTADDR, 1); err != nil { - l.logger.Errorf("SetsockoptInt(SOL_IPV6, IPV6_RECVORIGDSTADDR, 1): %v", err) - } + if err := unix.SetsockoptInt(int(fd), unix.SOL_IP, unix.IP_TRANSPARENT, 1); err != nil { + l.logger.Errorf("SetsockoptInt(SOL_IP, IP_TRANSPARENT, 1): %v", err) + } + if err := unix.SetsockoptInt(int(fd), unix.SOL_IP, unix.IP_RECVORIGDSTADDR, 1); err != nil { + l.logger.Errorf("SetsockoptInt(SOL_IP, IP_RECVORIGDSTADDR, 1): %v", err) + } + if err := unix.SetsockoptInt(int(fd), unix.SOL_IPV6, unix.IPV6_TRANSPARENT, 1); err != nil { + l.logger.Errorf("SetsockoptInt(SOL_IPV6, IPV6_TRANSPARENT, 1): %v", err) + } + if err := unix.SetsockoptInt(int(fd), unix.SOL_IPV6, unix.IPV6_RECVORIGDSTADDR, 1); err != nil { + l.logger.Errorf("SetsockoptInt(SOL_IPV6, IPV6_RECVORIGDSTADDR, 1): %v", err) } }) }, @@ -244,11 +243,14 @@ func udpAddrToSocketAddr(addr *net.UDPAddr) (unix.Sockaddr, error) { ip := [16]byte{} copy(ip[:], addr.IP.To16()) + var err error var zoneID uint64 if addr.Zone != "" { - zoneID, _ = strconv.ParseUint(addr.Zone, 10, 32) - if zoneID == 0 { - zoneID = 2 + zoneID, err = strconv.ParseUint(addr.Zone, 10, 32) + if err != nil { + if itf, _ := net.InterfaceByName(addr.Zone); itf != nil { + zoneID = uint64(itf.Index) + } } } From c0a80400d221204d8d41c24dda0b619052b3c4e5 Mon Sep 17 00:00:00 2001 From: ginuerzh Date: Mon, 15 Jul 2024 20:34:59 +0800 Subject: [PATCH 9/9] add support for icmpv6 --- dialer/icmp/dialer.go | 23 ++++++++++++++-- dialer/icmp/metadata.go | 15 +++-------- handler/redirect/tcp/metadata.go | 8 ++---- internal/util/icmp/conn.go | 44 +++++++++++++++++++++++++------ listener/icmp/listener.go | 22 ++++++++++++++-- listener/icmp/metadata.go | 19 ++++--------- listener/redirect/tcp/metadata.go | 5 +--- 7 files changed, 89 insertions(+), 47 deletions(-) diff --git a/dialer/icmp/dialer.go b/dialer/icmp/dialer.go index 85a5385..05a311d 100644 --- a/dialer/icmp/dialer.go +++ b/dialer/icmp/dialer.go @@ -19,9 +19,11 @@ import ( func init() { registry.DialerRegistry().Register("icmp", NewDialer) + registry.DialerRegistry().Register("icmp6", NewDialer6) } type icmpDialer struct { + ip6 bool sessions map[string]*quicSession sessionMutex sync.Mutex logger logger.Logger @@ -42,6 +44,19 @@ func NewDialer(opts ...dialer.Option) dialer.Dialer { } } +func NewDialer6(opts ...dialer.Option) dialer.Dialer { + options := dialer.Options{} + for _, opt := range opts { + opt(&options) + } + + return &icmpDialer{ + ip6: true, + sessions: make(map[string]*quicSession), + logger: options.Logger, + options: options, + } +} func (d *icmpDialer) Init(md md.Metadata) (err error) { if err = d.parseMetadata(md); err != nil { return @@ -71,7 +86,11 @@ func (d *icmpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO } var pc net.PacketConn - pc, err = icmp.ListenPacket("ip4:icmp", "") + if d.ip6 { + pc, err = icmp.ListenPacket("ip6:ipv6-icmp", "") + } else { + pc, err = icmp.ListenPacket("ip4:icmp", "") + } if err != nil { return } @@ -81,7 +100,7 @@ func (d *icmpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO id = rand.New(rand.NewSource(time.Now().UnixNano())).Intn(math.MaxUint16) + 1 raddr.Port = id } - pc = icmp_pkg.ClientConn(pc, id) + pc = icmp_pkg.ClientConn(d.ip6, pc, id) session, err = d.initSession(ctx, raddr, pc) if err != nil { diff --git a/dialer/icmp/metadata.go b/dialer/icmp/metadata.go index 6d3b784..da1448f 100644 --- a/dialer/icmp/metadata.go +++ b/dialer/icmp/metadata.go @@ -14,21 +14,14 @@ type metadata struct { } func (d *icmpDialer) parseMetadata(md mdata.Metadata) (err error) { - const ( - keepAlive = "keepAlive" - keepAlivePeriod = "ttl" - handshakeTimeout = "handshakeTimeout" - maxIdleTimeout = "maxIdleTimeout" - ) - - if mdutil.GetBool(md, keepAlive) { - d.md.keepAlivePeriod = mdutil.GetDuration(md, keepAlivePeriod) + if mdutil.GetBool(md, "keepalive") { + d.md.keepAlivePeriod = mdutil.GetDuration(md, "ttl") if d.md.keepAlivePeriod <= 0 { d.md.keepAlivePeriod = 10 * time.Second } } - d.md.handshakeTimeout = mdutil.GetDuration(md, handshakeTimeout) - d.md.maxIdleTimeout = mdutil.GetDuration(md, maxIdleTimeout) + d.md.handshakeTimeout = mdutil.GetDuration(md, "handshakeTimeout") + d.md.maxIdleTimeout = mdutil.GetDuration(md, "maxIdleTimeout") return } diff --git a/handler/redirect/tcp/metadata.go b/handler/redirect/tcp/metadata.go index 1e319d7..8206eb1 100644 --- a/handler/redirect/tcp/metadata.go +++ b/handler/redirect/tcp/metadata.go @@ -14,12 +14,8 @@ type metadata struct { } func (h *redirectHandler) parseMetadata(md mdata.Metadata) (err error) { - const ( - tproxy = "tproxy" - sniffing = "sniffing" - ) - h.md.tproxy = mdutil.GetBool(md, tproxy) - h.md.sniffing = mdutil.GetBool(md, sniffing) + h.md.tproxy = mdutil.GetBool(md, "tproxy") + h.md.sniffing = mdutil.GetBool(md, "sniffing") h.md.sniffingTimeout = mdutil.GetDuration(md, "sniffing.timeout") return } diff --git a/internal/util/icmp/conn.go b/internal/util/icmp/conn.go index 593e421..2fd50a2 100644 --- a/internal/util/icmp/conn.go +++ b/internal/util/icmp/conn.go @@ -12,6 +12,12 @@ import ( "github.com/go-gost/core/logger" "golang.org/x/net/icmp" "golang.org/x/net/ipv4" + "golang.org/x/net/ipv6" +) + +const ( + ICMPv4 = 1 + ICMPv6 = 58 ) const ( @@ -79,13 +85,15 @@ func (m *message) Decode(b []byte) (n int, err error) { } type clientConn struct { + ip6 bool net.PacketConn id int seq uint32 } -func ClientConn(conn net.PacketConn, id int) net.PacketConn { +func ClientConn(ip6 bool, conn net.PacketConn, id int) net.PacketConn { return &clientConn{ + ip6: ip6, PacketConn: conn, id: id, } @@ -101,13 +109,17 @@ func (c *clientConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) { return } - m, err := icmp.ParseMessage(1, buf[:n]) + proto := ICMPv4 + if c.ip6 { + proto = ICMPv6 + } + m, err := icmp.ParseMessage(proto, buf[:n]) if err != nil { // logger.Default().Error("icmp: parse message %v", err) return 0, addr, err } echo, ok := m.Body.(*icmp.Echo) - if !ok || m.Type != ipv4.ICMPTypeEchoReply { + if !ok || m.Type != ipv4.ICMPTypeEchoReply && m.Type != ipv6.ICMPTypeEchoReply { // logger.Default().Warnf("icmp: invalid type %s (discarded)", m.Type) continue // discard } @@ -135,6 +147,7 @@ func (c *clientConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) { addr = &net.UDPAddr{ IP: v.IP, Port: c.id, + Zone: v.Zone, } } // logger.Default().Infof("icmp: read from: %v %d", addr, n) @@ -146,7 +159,7 @@ func (c *clientConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { // logger.Default().Infof("icmp: write to: %v %d", addr, len(b)) switch v := addr.(type) { case *net.UDPAddr: - addr = &net.IPAddr{IP: v.IP} + addr = &net.IPAddr{IP: v.IP, Zone: v.Zone} } buf := bufpool.Get(writeBufferSize) @@ -170,6 +183,10 @@ func (c *clientConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { Code: 0, Body: &echo, } + if c.ip6 { + m.Type = ipv6.ICMPTypeEchoRequest + } + wb, err := m.Marshal(nil) if err != nil { return 0, err @@ -180,12 +197,14 @@ func (c *clientConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { } type serverConn struct { + ip6 bool net.PacketConn seqs [65535]uint32 } -func ServerConn(conn net.PacketConn) net.PacketConn { +func ServerConn(ip6 bool, conn net.PacketConn) net.PacketConn { return &serverConn{ + ip6: ip6, PacketConn: conn, } } @@ -200,14 +219,18 @@ func (c *serverConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) { return } - m, err := icmp.ParseMessage(1, buf[:n]) + proto := ICMPv4 + if c.ip6 { + proto = ICMPv6 + } + m, err := icmp.ParseMessage(proto, buf[:n]) if err != nil { // logger.Default().Error("icmp: parse message %v", err) return 0, addr, err } echo, ok := m.Body.(*icmp.Echo) - if !ok || m.Type != ipv4.ICMPTypeEcho || echo.ID <= 0 { + if !ok || echo.ID <= 0 || m.Type != ipv4.ICMPTypeEcho && m.Type != ipv6.ICMPTypeEchoRequest { // logger.Default().Warnf("icmp: invalid type %s (discarded)", m.Type) continue } @@ -229,6 +252,7 @@ func (c *serverConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) { addr = &net.UDPAddr{ IP: v.IP, Port: echo.ID, + Zone: v.Zone, } } break @@ -244,7 +268,7 @@ func (c *serverConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { var id int switch v := addr.(type) { case *net.UDPAddr: - addr = &net.IPAddr{IP: v.IP} + addr = &net.IPAddr{IP: v.IP, Zone: v.Zone} id = v.Port } @@ -275,6 +299,10 @@ func (c *serverConn) WriteTo(b []byte, addr net.Addr) (n int, err error) { Code: 0, Body: &echo, } + if c.ip6 { + m.Type = ipv6.ICMPTypeEchoReply + } + wb, err := m.Marshal(nil) if err != nil { return 0, err diff --git a/listener/icmp/listener.go b/listener/icmp/listener.go index 86529f6..adc8497 100644 --- a/listener/icmp/listener.go +++ b/listener/icmp/listener.go @@ -19,9 +19,11 @@ import ( func init() { registry.ListenerRegistry().Register("icmp", NewListener) + registry.ListenerRegistry().Register("icmp6", NewListener6) } type icmpListener struct { + ip6 bool ln quic.EarlyListener cqueue chan net.Conn errChan chan error @@ -41,6 +43,18 @@ func NewListener(opts ...listener.Option) listener.Listener { } } +func NewListener6(opts ...listener.Option) listener.Listener { + options := listener.Options{} + for _, opt := range opts { + opt(&options) + } + return &icmpListener{ + ip6: true, + logger: options.Logger, + options: options, + } +} + func (l *icmpListener) Init(md md.Metadata) (err error) { if err = l.parseMetadata(md); err != nil { return @@ -52,11 +66,15 @@ func (l *icmpListener) Init(md md.Metadata) (err error) { } var conn net.PacketConn - conn, err = icmp.ListenPacket("ip4:icmp", addr) + if l.ip6 { + conn, err = icmp.ListenPacket("ip6:ipv6-icmp", addr) + } else { + conn, err = icmp.ListenPacket("ip4:icmp", addr) + } if err != nil { return } - conn = icmp_pkg.ServerConn(conn) + conn = icmp_pkg.ServerConn(l.ip6, conn) conn = metrics.WrapPacketConn(l.options.Service, conn) conn = stats.WrapPacketConn(conn, l.options.Stats) conn = admission.WrapPacketConn(l.options.Admission, conn) diff --git a/listener/icmp/metadata.go b/listener/icmp/metadata.go index 8cb029a..c6f704b 100644 --- a/listener/icmp/metadata.go +++ b/listener/icmp/metadata.go @@ -20,28 +20,19 @@ type metadata struct { } func (l *icmpListener) parseMetadata(md mdata.Metadata) (err error) { - const ( - keepAlive = "keepAlive" - keepAlivePeriod = "ttl" - handshakeTimeout = "handshakeTimeout" - maxIdleTimeout = "maxIdleTimeout" - - backlog = "backlog" - ) - - l.md.backlog = mdutil.GetInt(md, backlog) + l.md.backlog = mdutil.GetInt(md, "backlog") if l.md.backlog <= 0 { l.md.backlog = defaultBacklog } - if mdutil.GetBool(md, keepAlive) { - l.md.keepAlivePeriod = mdutil.GetDuration(md, keepAlivePeriod) + if mdutil.GetBool(md, "keepalive") { + l.md.keepAlivePeriod = mdutil.GetDuration(md, "ttl") if l.md.keepAlivePeriod <= 0 { l.md.keepAlivePeriod = 10 * time.Second } } - l.md.handshakeTimeout = mdutil.GetDuration(md, handshakeTimeout) - l.md.maxIdleTimeout = mdutil.GetDuration(md, maxIdleTimeout) + l.md.handshakeTimeout = mdutil.GetDuration(md, "handshakeTimeout") + l.md.maxIdleTimeout = mdutil.GetDuration(md, "maxIdleTimeout") return } diff --git a/listener/redirect/tcp/metadata.go b/listener/redirect/tcp/metadata.go index d06d41f..49180ef 100644 --- a/listener/redirect/tcp/metadata.go +++ b/listener/redirect/tcp/metadata.go @@ -11,10 +11,7 @@ type metadata struct { } func (l *redirectListener) parseMetadata(md mdata.Metadata) (err error) { - const ( - tproxy = "tproxy" - ) - l.md.tproxy = mdutil.GetBool(md, tproxy) + l.md.tproxy = mdutil.GetBool(md, "tproxy") l.md.mptcp = mdutil.GetBool(md, "mptcp") return }