move pkg to github.com/go-gost/core

This commit is contained in:
ginuerzh 2022-03-16 20:38:26 +08:00
parent 0a41b70ef4
commit 2864480296
114 changed files with 106 additions and 8330 deletions

View File

@ -1,4 +1,4 @@
FROM --platform=$BUILDPLATFORM golang:1.18rc1-alpine as builder
FROM --platform=$BUILDPLATFORM golang:1.18-alpine as builder
# Convert TARGETPLATFORM to GOARCH format
# https://github.com/tonistiigi/xx

View File

@ -10,8 +10,8 @@ import (
"time"
"github.com/go-gost/config"
"github.com/go-gost/gost/v3/pkg/metadata"
"github.com/go-gost/gost/v3/pkg/registry"
"github.com/go-gost/core/metadata"
"github.com/go-gost/core/registry"
)
var (
@ -34,14 +34,12 @@ func buildConfigFromCmd(services, nodes stringList) (*config.Config, error) {
if v := os.Getenv("GOST_PROFILING"); v != "" {
cfg.Profiling = &config.ProfilingConfig{
Enable: true,
Addr: v,
Addr: v,
}
}
if v := os.Getenv("GOST_METRICS"); v != "" {
cfg.Metrics = &config.MetricsConfig{
Enable: true,
Addr: v,
Addr: v,
}
}

View File

@ -7,9 +7,9 @@ import (
"github.com/go-gost/api"
"github.com/go-gost/config"
"github.com/go-gost/config/parsing"
"github.com/go-gost/gost/v3/pkg/logger"
"github.com/go-gost/gost/v3/pkg/registry"
"github.com/go-gost/gost/v3/pkg/service"
"github.com/go-gost/core/logger"
"github.com/go-gost/core/registry"
"github.com/go-gost/core/service"
metrics "github.com/go-gost/metrics/service"
)

View File

@ -9,7 +9,7 @@ import (
"runtime"
"github.com/go-gost/config"
"github.com/go-gost/gost/v3/pkg/logger"
"github.com/go-gost/core/logger"
"github.com/go-gost/metrics"
)
@ -63,8 +63,7 @@ func main() {
}
if metricsAddr != "" {
cfg.Metrics = &config.MetricsConfig{
Enable: true,
Addr: metricsAddr,
Addr: metricsAddr,
}
}
} else {
@ -89,7 +88,7 @@ func main() {
os.Exit(0)
}
if cfg.Profiling != nil && cfg.Profiling.Enable {
if cfg.Profiling != nil {
go func() {
addr := cfg.Profiling.Addr
if addr == "" {
@ -113,7 +112,7 @@ func main() {
}()
}
if cfg.Metrics != nil && cfg.Metrics.Enable {
if cfg.Metrics != nil {
metrics.SetGlobal(metrics.NewMetrics())
if cfg.Metrics.Addr != "" {
s, err := buildMetricsService(cfg.Metrics)

View File

@ -2,27 +2,27 @@ package main
import (
// core components
_ "github.com/go-gost/gost/v3/pkg/connector/forward"
_ "github.com/go-gost/gost/v3/pkg/connector/http"
_ "github.com/go-gost/gost/v3/pkg/connector/socks/v4"
_ "github.com/go-gost/gost/v3/pkg/connector/socks/v5"
_ "github.com/go-gost/core/connector/forward"
_ "github.com/go-gost/core/connector/http"
_ "github.com/go-gost/core/connector/socks/v4"
_ "github.com/go-gost/core/connector/socks/v5"
_ "github.com/go-gost/gost/v3/pkg/dialer/tcp"
_ "github.com/go-gost/gost/v3/pkg/dialer/tls"
_ "github.com/go-gost/gost/v3/pkg/dialer/udp"
_ "github.com/go-gost/core/dialer/tcp"
_ "github.com/go-gost/core/dialer/tls"
_ "github.com/go-gost/core/dialer/udp"
_ "github.com/go-gost/gost/v3/pkg/handler/auto"
_ "github.com/go-gost/gost/v3/pkg/handler/forward/local"
_ "github.com/go-gost/gost/v3/pkg/handler/forward/remote"
_ "github.com/go-gost/gost/v3/pkg/handler/http"
_ "github.com/go-gost/gost/v3/pkg/handler/socks/v4"
_ "github.com/go-gost/gost/v3/pkg/handler/socks/v5"
_ "github.com/go-gost/core/handler/auto"
_ "github.com/go-gost/core/handler/forward/local"
_ "github.com/go-gost/core/handler/forward/remote"
_ "github.com/go-gost/core/handler/http"
_ "github.com/go-gost/core/handler/socks/v4"
_ "github.com/go-gost/core/handler/socks/v5"
_ "github.com/go-gost/gost/v3/pkg/listener/rtcp"
_ "github.com/go-gost/gost/v3/pkg/listener/rudp"
_ "github.com/go-gost/gost/v3/pkg/listener/tcp"
_ "github.com/go-gost/gost/v3/pkg/listener/tls"
_ "github.com/go-gost/gost/v3/pkg/listener/udp"
_ "github.com/go-gost/core/listener/rtcp"
_ "github.com/go-gost/core/listener/rudp"
_ "github.com/go-gost/core/listener/tcp"
_ "github.com/go-gost/core/listener/tls"
_ "github.com/go-gost/core/listener/udp"
// extended components
_ "github.com/go-gost/x/connector/http2"

View File

@ -11,7 +11,7 @@ import (
"time"
"github.com/go-gost/config"
tls_util "github.com/go-gost/gost/v3/pkg/common/util/tls"
tls_util "github.com/go-gost/core/common/util/tls"
)
func buildDefaultTLSConfig(cfg *config.TLSConfig) {

48
go.mod
View File

@ -4,24 +4,20 @@ go 1.18
replace github.com/templexxx/cpu v0.0.7 => github.com/templexxx/cpu v0.0.10-0.20211111114238-98168dcec14a
replace github.com/go-gost/metrics => /home/gerry/code/go-gost/metrics
require (
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
github.com/go-gost/api v0.0.0-20220314132010-fb9f62042a6a
github.com/go-gost/config v0.0.0-20220314131548-69ef8eeb9733
github.com/go-gost/gosocks4 v0.0.1
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09
github.com/go-gost/metrics v0.0.0-20220314135054-2263ae431a5f
github.com/go-gost/x v0.0.0-20220315075737-506142fd0b72
github.com/gobwas/glob v0.2.3
github.com/miekg/dns v1.1.45
github.com/sirupsen/logrus v1.8.1
github.com/xtaci/smux v1.5.16
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5
github.com/go-gost/api v0.0.0-20220316115521-ab4ad0f12488
github.com/go-gost/config v0.0.0-20220316114445-04016a392b1e
github.com/go-gost/core v0.0.0-20220316114029-7db81fcfeb66
github.com/go-gost/metrics v0.0.0-20220316114656-e95ab1c62ea8
github.com/go-gost/x v0.0.0-20220316120634-36947bcbd9f3
)
require (
github.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed // indirect
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cheekybits/genny v1.0.0 // indirect
@ -31,13 +27,15 @@ require (
github.com/gin-contrib/cors v1.3.1 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.7.7 // indirect
github.com/go-gost/gost v0.0.0-20220312160816-bfc1f8472cc2 // indirect
github.com/go-gost/gosocks4 v0.0.1 // indirect
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09 // indirect
github.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7 // indirect
github.com/go-gost/tls-dissector v0.0.2-0.20211125135007-2b5d5bd9c07e // indirect
github.com/go-playground/locales v0.13.0 // indirect
github.com/go-playground/universal-translator v0.17.0 // indirect
github.com/go-playground/validator/v10 v10.4.1 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.10.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/gopacket v1.1.19 // indirect
@ -46,7 +44,7 @@ require (
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid v1.3.1 // indirect
github.com/klauspost/reedsolomon v1.9.9 // indirect
github.com/leodido/go-urn v1.2.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/lucas-clemente/quic-go v0.25.0 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/marten-seemann/qpack v0.2.1 // indirect
@ -55,6 +53,7 @@ require (
github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/miekg/dns v1.1.47 // indirect
github.com/milosgajdos/tenus v0.0.3 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 // indirect
@ -72,24 +71,27 @@ require (
github.com/rs/xid v1.3.0 // indirect
github.com/shadowsocks/go-shadowsocks2 v0.1.5 // indirect
github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 // indirect
github.com/spf13/afero v1.8.2 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.10.0 // indirect
github.com/spf13/viper v1.10.1 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/templexxx/cpu v0.0.7 // indirect
github.com/templexxx/xorsimd v0.4.1 // indirect
github.com/tjfoc/gmsm v1.3.2 // indirect
github.com/ugorji/go/codec v1.1.7 // indirect
github.com/ugorji/go/codec v1.2.7 // indirect
github.com/xtaci/kcp-go/v5 v5.6.1 // indirect
github.com/xtaci/smux v1.5.16 // indirect
github.com/xtaci/tcpraw v1.2.25 // indirect
golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70 // indirect
golang.org/x/mod v0.5.1 // indirect
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.9 // indirect
golang.org/x/tools v0.1.10 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350 // indirect
google.golang.org/grpc v1.45.0 // indirect

84
go.sum
View File

@ -66,6 +66,7 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
@ -87,6 +88,7 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH
github.com/coreos/go-iptables v0.5.0 h1:mw6SAibtHKZcNzAsOxjoHIG0gy5YFHhypWSSNc6EjbQ=
github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -120,26 +122,22 @@ github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gost/api v0.0.0-20220314132010-fb9f62042a6a h1:omtLcSi8zs+Bi5eFZt16S364qnzADN0XaRlXysbjuek=
github.com/go-gost/api v0.0.0-20220314132010-fb9f62042a6a/go.mod h1:KSqpPSLY4nzNKU1iitZsGghXECh+afmexaCOUWewgT4=
github.com/go-gost/config v0.0.0-20220314131548-69ef8eeb9733 h1:TrBS1GYkM0WZBEMBlzHzB5lwp/VEosB4JwyhAApVnYY=
github.com/go-gost/config v0.0.0-20220314131548-69ef8eeb9733/go.mod h1:bz3HBlZwBiyiRmHcEgrxls6c824tubyw7FcSVgs75z8=
github.com/go-gost/api v0.0.0-20220316115521-ab4ad0f12488 h1:c1hthEceq5yiXwZEdX73e4dj4BRxNxFQfSXXDR1wL2Y=
github.com/go-gost/api v0.0.0-20220316115521-ab4ad0f12488/go.mod h1:TfLdWhO3b86fPiVOOtorOqhhZVSYwNS8C0BmarYaHtg=
github.com/go-gost/config v0.0.0-20220316114445-04016a392b1e h1:0tQmXLljIgBxA1ghQ80HoF6RGlzLdkOirHjAgvGU4NA=
github.com/go-gost/config v0.0.0-20220316114445-04016a392b1e/go.mod h1:TcXyaEcav8QK85dnpt+2piewCGLsUQ3W0D8SFTcRdAA=
github.com/go-gost/core v0.0.0-20220316114029-7db81fcfeb66 h1:c2xJRqXxys5FSi66dK5xSxuz8IeoYy2N+uOPse4OooE=
github.com/go-gost/core v0.0.0-20220316114029-7db81fcfeb66/go.mod h1:WyaY3XWXX0SHgao6DP8/YnSnzyMTJEWdyzegDWNSQFY=
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.3.1-0.20211109033403-d894d75b7f09 h1:A95M6UWcfZgOuJkQ7QLfG0Hs5peWIUSysCDNz4pfe04=
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09/go.mod h1:1G6I7HP7VFVxveGkoK8mnprnJqSqJjdcASKsdUn4Pp4=
github.com/go-gost/gost v0.0.0-20220312160816-bfc1f8472cc2 h1:yTxlvQ99SsSylNHv7A7v739k0gKbW9sqquXPlJX/EGE=
github.com/go-gost/gost v0.0.0-20220312160816-bfc1f8472cc2/go.mod h1:d7OMWKlZWgWarOEqYEsicSOjplSPMpoOpC8wCqMkBjI=
github.com/go-gost/metrics v0.0.0-20220314135054-2263ae431a5f h1:gNquUvOvPXUpq4Xk7ed7motbVN5t0HMqImf96k+pzlU=
github.com/go-gost/metrics v0.0.0-20220314135054-2263ae431a5f/go.mod h1:Ac2Pigx5GMJEznkP9wLdBJ36+rYwWiJPqWk7lrg3FKg=
github.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7 h1:itaaJhQJ19kUXEB4Igb0EbY8m+1Py2AaNNSBds/9gk4=
github.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7/go.mod h1:lcX+23LCQ3khIeASBo+tJ/WbwXFO32/N5YN6ucuYTG8=
github.com/go-gost/tls-dissector v0.0.2-0.20211125135007-2b5d5bd9c07e h1:73NGqAs22ey3wJkIYVD/ACEoovuIuOlEzQTEoqrO5+U=
github.com/go-gost/tls-dissector v0.0.2-0.20211125135007-2b5d5bd9c07e/go.mod h1:/9QfdewqmHdaE362Hv5nDaSWLx3pCmtD870d6GaquXs=
github.com/go-gost/x v0.0.0-20220315074522-073ad80ce42d h1:WBw+yVkD5GTu30Cz5l4spRJxQJajo+DRzCQWPaI6h08=
github.com/go-gost/x v0.0.0-20220315074522-073ad80ce42d/go.mod h1:9yCS6ulkZHRgYe5yjih0Tn0wo+2kXy4tjABfQFTOius=
github.com/go-gost/x v0.0.0-20220315075737-506142fd0b72 h1:2gj6BwCUpn+5FJsV+xxGUY60HXCQO6eVU6leEcY1rts=
github.com/go-gost/x v0.0.0-20220315075737-506142fd0b72/go.mod h1:9yCS6ulkZHRgYe5yjih0Tn0wo+2kXy4tjABfQFTOius=
github.com/go-gost/x v0.0.0-20220316120634-36947bcbd9f3 h1:rweJPJBr3k6wqzUgLbEMeoJq/AUa5koaUaqjp+rvk6I=
github.com/go-gost/x v0.0.0-20220316120634-36947bcbd9f3/go.mod h1:CEpilNHCLNGQO2sGEJMxTqPJIj1zJ7q3BuzSQU4CyG8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
@ -149,13 +147,16 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-playground/validator/v10 v10.10.1 h1:uA0+amWMiglNZKZ9FJRKUAe9U3RX91eVn1JYXMWt7ig=
github.com/go-playground/validator/v10 v10.10.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
@ -269,15 +270,19 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/lucas-clemente/quic-go v0.25.0 h1:K+X9Gvd7JXsOHtU0N2icZ2Nw3rx82uBej3mP4CLgibc=
github.com/lucas-clemente/quic-go v0.25.0/go.mod h1:YtzP8bxRVCBlO77yRanE264+fY/T2U9ZlW1AaHOsMOg=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
@ -300,8 +305,8 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.1.45 h1:g5fRIhm9nx7g8osrAvgb16QJfmyMsyOCb+J7LSv+Qzk=
github.com/miekg/dns v1.1.45/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/miekg/dns v1.1.47 h1:J9bWiXbqMbnZPcY8Qi2E3EWIBsIm6MZzzJB9VRg5gL8=
github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/milosgajdos/tenus v0.0.3 h1:jmaJzwaY1DUyYVD0lM4U+uvP2kkEg1VahDqRFxIkVBE=
github.com/milosgajdos/tenus v0.0.3/go.mod h1:eIjx29vNeDOYWJuCnaHY2r4fq5egetV26ry3on7p8qY=
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
@ -336,6 +341,7 @@ github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je4
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@ -372,6 +378,9 @@ github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4=
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
@ -420,8 +429,8 @@ github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmq
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.10.0 h1:mXH0UwHS4D2HwWZa75im4xIQynLfblmWV7qcWpfv0yk=
github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
github.com/spf13/viper v1.10.1 h1:nuJZuYpG7gTj/XqiUwg8bA0cp1+M2mC3J4g5luUYBKk=
github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@ -442,8 +451,10 @@ github.com/templexxx/xorsimd v0.4.1/go.mod h1:W+ffZz8jJMH2SXwuKu9WhygqBMbFnp14G2
github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
github.com/xtaci/kcp-go/v5 v5.6.1 h1:Pwn0aoeNSPF9dTS7IgiPXn0HEtaIlVb6y5UKWPsx8bI=
@ -484,8 +495,9 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/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-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70 h1:syTAU9FwmvzEoIYMqcPHOcVm4H3U5u90WsvuYgwpETU=
golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38=
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -521,8 +533,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -565,8 +577,9 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -650,9 +663,10 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 h1:y/woIyUBFbpQGKS0u1aHF/40WUDnek3fPOyD08H5Vng=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 h1:A9i04dxx7Cribqbs8jf3FQLogkL/CV2YN7hj9KWJCkc=
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
@ -724,8 +738,8 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.9 h1:j9KsMiaP1c3B0OTQGth0/k+miLGTgLsAFUCrF2vLcF8=
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
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=
@ -846,8 +860,9 @@ google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
@ -868,6 +883,7 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@ -1,89 +0,0 @@
package admission
import (
"net"
"strconv"
"github.com/go-gost/gost/v3/pkg/common/matcher"
"github.com/go-gost/gost/v3/pkg/logger"
)
type Admission interface {
Admit(addr string) bool
}
type options struct {
logger logger.Logger
}
type Option func(opts *options)
func LoggerOption(logger logger.Logger) Option {
return func(opts *options) {
opts.logger = logger
}
}
type admission struct {
matchers []matcher.Matcher
reversed bool
options options
}
// NewAdmission creates and initializes a new Admission using matchers as its match rules.
// The rules will be reversed if the reversed is true.
func NewAdmission(reversed bool, matchers []matcher.Matcher, opts ...Option) Admission {
options := options{}
for _, opt := range opts {
opt(&options)
}
return &admission{
matchers: matchers,
reversed: reversed,
options: options,
}
}
// NewAdmissionPatterns creates and initializes a new Admission using matcher patterns as its match rules.
// The rules will be reversed if the reverse is true.
func NewAdmissionPatterns(reversed bool, patterns []string, opts ...Option) Admission {
var matchers []matcher.Matcher
for _, pattern := range patterns {
if m := matcher.NewMatcher(pattern); m != nil {
matchers = append(matchers, m)
}
}
return NewAdmission(reversed, matchers, opts...)
}
func (p *admission) Admit(addr string) bool {
if addr == "" || p == nil || len(p.matchers) == 0 {
p.options.logger.Debugf("admission: %v is denied", addr)
return false
}
// try to strip the port
if host, port, _ := net.SplitHostPort(addr); host != "" && port != "" {
if p, _ := strconv.Atoi(port); p > 0 { // port is valid
addr = host
}
}
var matched bool
for _, matcher := range p.matchers {
if matcher == nil {
continue
}
if matcher.Match(addr) {
matched = true
break
}
}
b := !p.reversed && matched ||
p.reversed && !matched
if !b {
p.options.logger.Debugf("admission: %v is denied", addr)
}
return b
}

View File

@ -1,223 +0,0 @@
package wrapper
import (
"errors"
"io"
"net"
"syscall"
"github.com/go-gost/gost/v3/pkg/admission"
)
var (
errUnsupport = errors.New("unsupported operation")
)
type packetConn struct {
net.PacketConn
admission admission.Admission
}
func WrapPacketConn(admission admission.Admission, pc net.PacketConn) net.PacketConn {
if admission == nil {
return pc
}
return &packetConn{
PacketConn: pc,
admission: admission,
}
}
func (c *packetConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
for {
n, addr, err = c.PacketConn.ReadFrom(p)
if err != nil {
return
}
if c.admission != nil &&
!c.admission.Admit(addr.String()) {
continue
}
return
}
}
type udpConn struct {
net.PacketConn
admission admission.Admission
}
func WrapUDPConn(admission admission.Admission, pc net.PacketConn) UDPConn {
return &udpConn{
PacketConn: pc,
admission: admission,
}
}
func (c *udpConn) RemoteAddr() net.Addr {
if nc, ok := c.PacketConn.(remoteAddr); ok {
return nc.RemoteAddr()
}
return nil
}
func (c *udpConn) SetReadBuffer(n int) error {
if nc, ok := c.PacketConn.(setBuffer); ok {
return nc.SetReadBuffer(n)
}
return errUnsupport
}
func (c *udpConn) SetWriteBuffer(n int) error {
if nc, ok := c.PacketConn.(setBuffer); ok {
return nc.SetWriteBuffer(n)
}
return errUnsupport
}
func (c *udpConn) Read(b []byte) (n int, err error) {
if nc, ok := c.PacketConn.(io.Reader); ok {
n, err = nc.Read(b)
return
}
err = errUnsupport
return
}
func (c *udpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
for {
n, addr, err = c.PacketConn.ReadFrom(p)
if err != nil {
return
}
if c.admission != nil &&
!c.admission.Admit(addr.String()) {
continue
}
return
}
}
func (c *udpConn) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) {
if nc, ok := c.PacketConn.(readUDP); ok {
for {
n, addr, err = nc.ReadFromUDP(b)
if err != nil {
return
}
if c.admission != nil &&
!c.admission.Admit(addr.String()) {
continue
}
return
}
}
err = errUnsupport
return
}
func (c *udpConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error) {
if nc, ok := c.PacketConn.(readUDP); ok {
for {
n, oobn, flags, addr, err = nc.ReadMsgUDP(b, oob)
if err != nil {
return
}
if c.admission != nil &&
!c.admission.Admit(addr.String()) {
continue
}
return
}
}
err = errUnsupport
return
}
func (c *udpConn) Write(b []byte) (n int, err error) {
if nc, ok := c.PacketConn.(io.Writer); ok {
n, err = nc.Write(b)
return
}
err = errUnsupport
return
}
func (c *udpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
n, err = c.PacketConn.WriteTo(p, addr)
return
}
func (c *udpConn) WriteToUDP(b []byte, addr *net.UDPAddr) (n int, err error) {
if nc, ok := c.PacketConn.(writeUDP); ok {
n, err = nc.WriteToUDP(b, addr)
return
}
err = errUnsupport
return
}
func (c *udpConn) WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error) {
if nc, ok := c.PacketConn.(writeUDP); ok {
n, oobn, err = nc.WriteMsgUDP(b, oob, addr)
return
}
err = errUnsupport
return
}
func (c *udpConn) SyscallConn() (rc syscall.RawConn, err error) {
if nc, ok := c.PacketConn.(syscallConn); ok {
return nc.SyscallConn()
}
err = errUnsupport
return
}
func (c *udpConn) SetDSCP(n int) error {
if nc, ok := c.PacketConn.(setDSCP); ok {
return nc.SetDSCP(n)
}
return nil
}
type UDPConn interface {
net.PacketConn
io.Reader
io.Writer
readUDP
writeUDP
setBuffer
syscallConn
remoteAddr
}
type setBuffer interface {
SetReadBuffer(bytes int) error
SetWriteBuffer(bytes int) error
}
type readUDP interface {
ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error)
ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error)
}
type writeUDP interface {
WriteToUDP(b []byte, addr *net.UDPAddr) (int, error)
WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error)
}
type syscallConn interface {
SyscallConn() (syscall.RawConn, error)
}
type remoteAddr interface {
RemoteAddr() net.Addr
}
// tcpraw.TCPConn
type setDSCP interface {
SetDSCP(int) error
}

View File

@ -1,37 +0,0 @@
package wrapper
import (
"net"
"github.com/go-gost/gost/v3/pkg/admission"
)
type listener struct {
net.Listener
admission admission.Admission
}
func WrapListener(admission admission.Admission, ln net.Listener) net.Listener {
if admission == nil {
return ln
}
return &listener{
Listener: ln,
admission: admission,
}
}
func (ln *listener) Accept() (net.Conn, error) {
for {
c, err := ln.Listener.Accept()
if err != nil {
return nil, err
}
if ln.admission != nil &&
!ln.admission.Admit(c.RemoteAddr().String()) {
c.Close()
continue
}
return c, err
}
}

View File

@ -1,28 +0,0 @@
package auth
// Authenticator is an interface for user authentication.
type Authenticator interface {
Authenticate(user, password string) bool
}
// authenticator is an Authenticator that authenticates client by key-value pairs.
type authenticator struct {
kvs map[string]string
}
// NewAuthenticator creates an Authenticator that authenticates client by pre-defined user mapping.
func NewAuthenticator(kvs map[string]string) Authenticator {
return &authenticator{
kvs: kvs,
}
}
// Authenticate checks the validity of the provided user-password pair.
func (au *authenticator) Authenticate(user, password string) bool {
if au == nil || len(au.kvs) == 0 {
return true
}
v, ok := au.kvs[user]
return ok && (v == "" || password == v)
}

View File

@ -1,90 +0,0 @@
package bypass
import (
"net"
"strconv"
"github.com/go-gost/gost/v3/pkg/common/matcher"
"github.com/go-gost/gost/v3/pkg/logger"
)
// Bypass is a filter of address (IP or domain).
type Bypass interface {
// Contains reports whether the bypass includes addr.
Contains(addr string) bool
}
type options struct {
logger logger.Logger
}
type Option func(opts *options)
func LoggerOption(logger logger.Logger) Option {
return func(opts *options) {
opts.logger = logger
}
}
type bypass struct {
matchers []matcher.Matcher
reversed bool
options options
}
// NewBypass creates and initializes a new Bypass using matchers as its match rules.
// The rules will be reversed if the reversed is true.
func NewBypass(reversed bool, matchers []matcher.Matcher, opts ...Option) Bypass {
options := options{}
for _, opt := range opts {
opt(&options)
}
return &bypass{
matchers: matchers,
reversed: reversed,
options: options,
}
}
// NewBypassPatterns creates and initializes a new Bypass using matcher patterns as its match rules.
// The rules will be reversed if the reverse is true.
func NewBypassPatterns(reversed bool, patterns []string, opts ...Option) Bypass {
var matchers []matcher.Matcher
for _, pattern := range patterns {
if m := matcher.NewMatcher(pattern); m != nil {
matchers = append(matchers, m)
}
}
return NewBypass(reversed, matchers, opts...)
}
func (bp *bypass) Contains(addr string) bool {
if addr == "" || bp == nil || len(bp.matchers) == 0 {
return false
}
// try to strip the port
if host, port, _ := net.SplitHostPort(addr); host != "" && port != "" {
if p, _ := strconv.Atoi(port); p > 0 { // port is valid
addr = host
}
}
var matched bool
for _, matcher := range bp.matchers {
if matcher == nil {
continue
}
if matcher.Match(addr) {
matched = true
break
}
}
b := !bp.reversed && matched ||
bp.reversed && !matched
if b {
bp.options.logger.Debugf("bypass: %s", addr)
}
return b
}

View File

@ -1,41 +0,0 @@
package chain
type Chainer interface {
Route(network, address string) *Route
}
type Chain struct {
groups []*NodeGroup
}
func (c *Chain) AddNodeGroup(group *NodeGroup) {
c.groups = append(c.groups, group)
}
func (c *Chain) Route(network, address string) (r *Route) {
if c == nil || len(c.groups) == 0 {
return
}
r = &Route{}
for _, group := range c.groups {
node := group.Next()
if node == nil {
return
}
if node.Bypass != nil && node.Bypass.Contains(address) {
break
}
if node.Transport.Multiplex() {
tr := node.Transport.Copy().
WithRoute(r)
node = node.Copy()
node.Transport = tr
r = &Route{}
}
r.addNode(node)
}
return r
}

View File

@ -1,97 +0,0 @@
package chain
import (
"sync/atomic"
"time"
"github.com/go-gost/gost/v3/pkg/bypass"
"github.com/go-gost/gost/v3/pkg/hosts"
"github.com/go-gost/gost/v3/pkg/resolver"
)
type Node struct {
Name string
Addr string
Transport *Transport
Bypass bypass.Bypass
Resolver resolver.Resolver
Hosts hosts.HostMapper
Marker *FailMarker
}
func (node *Node) Copy() *Node {
n := &Node{}
*n = *node
return n
}
type NodeGroup struct {
nodes []*Node
selector Selector
}
func NewNodeGroup(nodes ...*Node) *NodeGroup {
return &NodeGroup{
nodes: nodes,
}
}
func (g *NodeGroup) AddNode(node *Node) {
g.nodes = append(g.nodes, node)
}
func (g *NodeGroup) WithSelector(selector Selector) *NodeGroup {
g.selector = selector
return g
}
func (g *NodeGroup) Next() *Node {
if g == nil || len(g.nodes) == 0 {
return nil
}
s := g.selector
if s == nil {
s = DefaultSelector
}
return s.Select(g.nodes...)
}
type FailMarker struct {
failTime int64
failCount int64
}
func (m *FailMarker) FailTime() int64 {
if m == nil {
return 0
}
return atomic.LoadInt64(&m.failTime)
}
func (m *FailMarker) FailCount() int64 {
if m == nil {
return 0
}
return atomic.LoadInt64(&m.failCount)
}
func (m *FailMarker) Mark() {
if m == nil {
return
}
atomic.AddInt64(&m.failCount, 1)
atomic.StoreInt64(&m.failTime, time.Now().Unix())
}
func (m *FailMarker) Reset() {
if m == nil {
return
}
atomic.StoreInt64(&m.failCount, 0)
}

View File

@ -1,47 +0,0 @@
package chain
import (
"context"
"fmt"
"net"
"github.com/go-gost/gost/v3/pkg/hosts"
"github.com/go-gost/gost/v3/pkg/logger"
"github.com/go-gost/gost/v3/pkg/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, err := net.SplitHostPort(addr)
if err != nil {
return "", err
}
if host == "" {
return addr, nil
}
if hosts != nil {
if ips, _ := hosts.Lookup(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 exists", host)
}
return net.JoinHostPort(ips[0].String(), port), nil
}
return addr, nil
}

View File

@ -1,192 +0,0 @@
package chain
import (
"context"
"errors"
"fmt"
"net"
"time"
"github.com/go-gost/gost/v3/pkg/common/net/dialer"
"github.com/go-gost/gost/v3/pkg/common/util/udp"
"github.com/go-gost/gost/v3/pkg/connector"
"github.com/go-gost/gost/v3/pkg/logger"
)
var (
ErrEmptyRoute = errors.New("empty route")
)
type Route struct {
nodes []*Node
ifceName string
logger logger.Logger
}
func (r *Route) addNode(node *Node) {
r.nodes = append(r.nodes, node)
}
func (r *Route) Dial(ctx context.Context, network, address string) (net.Conn, error) {
if r.Len() == 0 {
netd := dialer.NetDialer{
Timeout: 30 * time.Second,
}
if r != nil {
netd.Interface = r.ifceName
}
return netd.Dial(ctx, network, address)
}
conn, err := r.connect(ctx)
if err != nil {
return nil, err
}
cc, err := r.GetNode(r.Len()-1).Transport.Connect(ctx, conn, network, address)
if err != nil {
conn.Close()
return nil, err
}
return cc, nil
}
func (r *Route) Bind(ctx context.Context, network, address string, opts ...connector.BindOption) (net.Listener, error) {
if r.Len() == 0 {
return r.bindLocal(ctx, network, address, opts...)
}
conn, err := r.connect(ctx)
if err != nil {
return nil, err
}
ln, err := r.GetNode(r.Len()-1).Transport.Bind(ctx, conn, network, address, opts...)
if err != nil {
conn.Close()
return nil, err
}
return ln, nil
}
func (r *Route) connect(ctx context.Context) (conn net.Conn, err error) {
if r.Len() == 0 {
return nil, ErrEmptyRoute
}
network := "ip"
node := r.nodes[0]
addr, err := resolve(ctx, network, node.Addr, node.Resolver, node.Hosts, r.logger)
if err != nil {
node.Marker.Mark()
return
}
cc, err := node.Transport.Dial(ctx, addr)
if err != nil {
node.Marker.Mark()
return
}
cn, err := node.Transport.Handshake(ctx, cc)
if err != nil {
cc.Close()
node.Marker.Mark()
return
}
node.Marker.Reset()
preNode := node
for _, node := range r.nodes[1:] {
addr, err = resolve(ctx, network, node.Addr, node.Resolver, node.Hosts, r.logger)
if err != nil {
cn.Close()
node.Marker.Mark()
return
}
cc, err = preNode.Transport.Connect(ctx, cn, "tcp", addr)
if err != nil {
cn.Close()
node.Marker.Mark()
return
}
cc, err = node.Transport.Handshake(ctx, cc)
if err != nil {
cn.Close()
node.Marker.Mark()
return
}
node.Marker.Reset()
cn = cc
preNode = node
}
conn = cn
return
}
func (r *Route) Len() int {
if r == nil {
return 0
}
return len(r.nodes)
}
func (r *Route) GetNode(index int) *Node {
if r.Len() == 0 || index < 0 || index >= len(r.nodes) {
return nil
}
return r.nodes[index]
}
func (r *Route) Path() (path []*Node) {
if r == nil || len(r.nodes) == 0 {
return nil
}
for _, node := range r.nodes {
if node.Transport != nil && node.Transport.route != nil {
path = append(path, node.Transport.route.Path()...)
}
path = append(path, node)
}
return
}
func (r *Route) bindLocal(ctx context.Context, network, address string, opts ...connector.BindOption) (net.Listener, error) {
options := connector.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, addr,
options.Backlog, options.UDPDataQueueSize, options.UDPDataBufferSize,
options.UDPConnTTL, logger)
return ln, err
default:
err := fmt.Errorf("network %s unsupported", network)
return nil, err
}
}

View File

@ -1,169 +0,0 @@
package chain
import (
"bytes"
"context"
"fmt"
"net"
"time"
"github.com/go-gost/gost/v3/pkg/connector"
"github.com/go-gost/gost/v3/pkg/hosts"
"github.com/go-gost/gost/v3/pkg/logger"
"github.com/go-gost/gost/v3/pkg/resolver"
)
type Router struct {
timeout time.Duration
retries int
ifceName string
chain Chainer
resolver resolver.Resolver
hosts hosts.HostMapper
logger logger.Logger
}
func (r *Router) WithTimeout(timeout time.Duration) *Router {
r.timeout = timeout
return r
}
func (r *Router) WithRetries(retries int) *Router {
r.retries = retries
return r
}
func (r *Router) WithInterface(ifceName string) *Router {
r.ifceName = ifceName
return r
}
func (r *Router) WithChain(chain Chainer) *Router {
r.chain = chain
return r
}
func (r *Router) WithResolver(resolver resolver.Resolver) *Router {
r.resolver = resolver
return r
}
func (r *Router) WithHosts(hosts hosts.HostMapper) *Router {
r.hosts = hosts
return r
}
func (r *Router) Hosts() hosts.HostMapper {
if r != nil {
return r.hosts
}
return nil
}
func (r *Router) WithLogger(logger logger.Logger) *Router {
r.logger = logger
return r
}
func (r *Router) Dial(ctx context.Context, network, address string) (conn net.Conn, err error) {
conn, err = r.dial(ctx, network, address)
if err != nil {
return
}
if network == "udp" || network == "udp4" || network == "udp6" {
if _, ok := conn.(net.PacketConn); !ok {
return &packetConn{conn}, nil
}
}
return
}
func (r *Router) dial(ctx context.Context, network, address string) (conn net.Conn, err error) {
count := r.retries + 1
if count <= 0 {
count = 1
}
r.logger.Debugf("dial %s/%s", address, network)
for i := 0; i < count; i++ {
var route *Route
if r.chain != nil {
route = r.chain.Route(network, address)
}
if r.logger.IsLevelEnabled(logger.DebugLevel) {
buf := bytes.Buffer{}
for _, node := range route.Path() {
fmt.Fprintf(&buf, "%s@%s > ", node.Name, node.Addr)
}
fmt.Fprintf(&buf, "%s", address)
r.logger.Debugf("route(retry=%d) %s", i, buf.String())
}
address, err = resolve(ctx, "ip", address, r.resolver, r.hosts, r.logger)
if err != nil {
r.logger.Error(err)
break
}
if route == nil {
route = &Route{}
}
route.ifceName = r.ifceName
route.logger = r.logger
conn, err = route.Dial(ctx, network, address)
if err == nil {
break
}
r.logger.Errorf("route(retry=%d) %s", i, err)
}
return
}
func (r *Router) Bind(ctx context.Context, network, address string, opts ...connector.BindOption) (ln net.Listener, err error) {
count := r.retries + 1
if count <= 0 {
count = 1
}
r.logger.Debugf("bind on %s/%s", address, network)
for i := 0; i < count; i++ {
var route *Route
if r.chain != nil {
route = r.chain.Route(network, address)
}
if r.logger.IsLevelEnabled(logger.DebugLevel) {
buf := bytes.Buffer{}
for _, node := range route.Path() {
fmt.Fprintf(&buf, "%s@%s > ", node.Name, node.Addr)
}
fmt.Fprintf(&buf, "%s", address)
r.logger.Debugf("route(retry=%d) %s", i, buf.String())
}
ln, err = route.Bind(ctx, network, address, opts...)
if err == nil {
break
}
r.logger.Errorf("route(retry=%d) %s", i, err)
}
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)
}

View File

@ -1,170 +0,0 @@
package chain
import (
"math/rand"
"net"
"strconv"
"sync"
"sync/atomic"
"time"
)
// default options for FailFilter
const (
DefaultFailTimeout = 30 * time.Second
)
var (
DefaultSelector = NewSelector(RoundRobinStrategy())
)
type Selector interface {
Select(nodes ...*Node) *Node
}
type selector struct {
strategy Strategy
filters []Filter
}
func NewSelector(strategy Strategy, filters ...Filter) Selector {
return &selector{
filters: filters,
strategy: strategy,
}
}
func (s *selector) Select(nodes ...*Node) *Node {
for _, filter := range s.filters {
nodes = filter.Filter(nodes...)
}
if len(nodes) == 0 {
return nil
}
return s.strategy.Apply(nodes...)
}
type Strategy interface {
Apply(nodes ...*Node) *Node
}
type roundRobinStrategy struct {
counter uint64
}
// RoundRobinStrategy is a strategy for node selector.
// The node will be selected by round-robin algorithm.
func RoundRobinStrategy() Strategy {
return &roundRobinStrategy{}
}
func (s *roundRobinStrategy) Apply(nodes ...*Node) *Node {
if len(nodes) == 0 {
return nil
}
n := atomic.AddUint64(&s.counter, 1) - 1
return nodes[int(n%uint64(len(nodes)))]
}
type randomStrategy struct {
rand *rand.Rand
mux sync.Mutex
}
// RandomStrategy is a strategy for node selector.
// The node will be selected randomly.
func RandomStrategy() Strategy {
return &randomStrategy{
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
}
}
func (s *randomStrategy) Apply(nodes ...*Node) *Node {
if len(nodes) == 0 {
return nil
}
s.mux.Lock()
defer s.mux.Unlock()
r := s.rand.Int()
return nodes[r%len(nodes)]
}
type fifoStrategy struct{}
// FIFOStrategy is a strategy for node selector.
// The node will be selected from first to last,
// and will stick to the selected node until it is failed.
func FIFOStrategy() Strategy {
return &fifoStrategy{}
}
// Apply applies the fifo strategy for the nodes.
func (s *fifoStrategy) Apply(nodes ...*Node) *Node {
if len(nodes) == 0 {
return nil
}
return nodes[0]
}
type Filter interface {
Filter(nodes ...*Node) []*Node
}
type failFilter struct {
maxFails int
failTimeout time.Duration
}
// FailFilter filters the dead node.
// A node is marked as dead if its failed count is greater than MaxFails.
func FailFilter(maxFails int, timeout time.Duration) Filter {
return &failFilter{
maxFails: maxFails,
failTimeout: timeout,
}
}
// Filter filters dead nodes.
func (f *failFilter) Filter(nodes ...*Node) []*Node {
maxFails := f.maxFails
failTimeout := f.failTimeout
if failTimeout == 0 {
failTimeout = DefaultFailTimeout
}
if len(nodes) <= 1 || maxFails <= 0 {
return nodes
}
var nl []*Node
for _, node := range nodes {
if node.Marker.FailCount() < int64(maxFails) ||
time.Since(time.Unix(node.Marker.FailTime(), 0)) >= failTimeout {
nl = append(nl, node)
}
}
return nl
}
type invalidFilter struct{}
// InvalidFilter filters the invalid node.
// A node is invalid if its port is invalid (negative or zero value).
func InvalidFilter() Filter {
return &invalidFilter{}
}
// Filter filters invalid nodes.
func (f *invalidFilter) Filter(nodes ...*Node) []*Node {
var nl []*Node
for _, node := range nodes {
_, sport, _ := net.SplitHostPort(node.Addr)
if port, _ := strconv.Atoi(sport); port > 0 {
nl = append(nl, node)
}
}
return nl
}

View File

@ -1,100 +0,0 @@
package chain
import (
"context"
"net"
"time"
net_dialer "github.com/go-gost/gost/v3/pkg/common/net/dialer"
"github.com/go-gost/gost/v3/pkg/connector"
"github.com/go-gost/gost/v3/pkg/dialer"
)
type Transport struct {
addr string
ifceName string
route *Route
dialer dialer.Dialer
connector connector.Connector
}
func (tr *Transport) Copy() *Transport {
tr2 := &Transport{}
*tr2 = *tr
return tr
}
func (tr *Transport) WithInterface(ifceName string) *Transport {
tr.ifceName = ifceName
return tr
}
func (tr *Transport) WithDialer(dialer dialer.Dialer) *Transport {
tr.dialer = dialer
return tr
}
func (tr *Transport) WithConnector(connector connector.Connector) *Transport {
tr.connector = connector
return tr
}
func (tr *Transport) Dial(ctx context.Context, addr string) (net.Conn, error) {
netd := &net_dialer.NetDialer{
Interface: tr.ifceName,
Timeout: 30 * time.Second,
}
if tr.route.Len() > 0 {
netd.DialFunc = func(ctx context.Context, network, addr string) (net.Conn, error) {
return tr.route.Dial(ctx, network, addr)
}
}
opts := []dialer.DialOption{
dialer.HostDialOption(tr.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.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) {
return tr.connector.Connect(ctx, conn, network, address)
}
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) WithRoute(r *Route) *Transport {
tr.route = r
return tr
}
func (tr *Transport) WithAddr(addr string) *Transport {
tr.addr = addr
return tr
}

View File

@ -1,113 +0,0 @@
package bufpool
import "sync"
var (
pools = []struct {
size int
pool sync.Pool
}{
{
size: 128,
pool: sync.Pool{
New: func() any {
b := make([]byte, 128)
return &b
},
},
},
{
size: 512,
pool: sync.Pool{
New: func() any {
b := make([]byte, 512)
return &b
},
},
},
{
size: 1024,
pool: sync.Pool{
New: func() any {
b := make([]byte, 1024)
return &b
},
},
},
{
size: 4096,
pool: sync.Pool{
New: func() any {
b := make([]byte, 4096)
return &b
},
},
},
{
size: 8192,
pool: sync.Pool{
New: func() any {
b := make([]byte, 8192)
return &b
},
},
},
{
size: 16 * 1024,
pool: sync.Pool{
New: func() any {
b := make([]byte, 16*1024)
return &b
},
},
},
{
size: 32 * 1024,
pool: sync.Pool{
New: func() any {
b := make([]byte, 32*1024)
return &b
},
},
},
{
size: 64 * 1024,
pool: sync.Pool{
New: func() any {
b := make([]byte, 64*1024)
return &b
},
},
},
{
size: 65 * 1024,
pool: sync.Pool{
New: func() any {
b := make([]byte, 65*1024)
return &b
},
},
},
}
)
// Get returns a buffer of specified size.
func Get(size int) *[]byte {
for i := range pools {
if size <= pools[i].size {
b := pools[i].pool.Get().(*[]byte)
*b = (*b)[:size]
return b
}
}
b := make([]byte, size)
return &b
}
func Put(b *[]byte) {
for i := range pools {
if cap(*b) == pools[i].size {
pools[i].pool.Put(b)
}
}
}

View File

@ -1,99 +0,0 @@
package matcher
import (
"net"
"strings"
"github.com/gobwas/glob"
)
// Matcher is a generic pattern matcher,
// it gives the match result of the given pattern for specific v.
type Matcher interface {
Match(v string) bool
}
// NewMatcher creates a Matcher for the given pattern.
// The acutal Matcher depends on the pattern:
// IP Matcher if pattern is a valid IP address.
// CIDR Matcher if pattern is a valid CIDR address.
// Domain Matcher if both of the above are not.
func NewMatcher(pattern string) Matcher {
if pattern == "" {
return nil
}
if ip := net.ParseIP(pattern); ip != nil {
return IPMatcher(ip)
}
if _, inet, err := net.ParseCIDR(pattern); err == nil {
return CIDRMatcher(inet)
}
return DomainMatcher(pattern)
}
type ipMatcher struct {
ip net.IP
}
// IPMatcher creates a Matcher for a specific IP address.
func IPMatcher(ip net.IP) Matcher {
return &ipMatcher{
ip: ip,
}
}
func (m *ipMatcher) Match(ip string) bool {
if m == nil {
return false
}
return m.ip.Equal(net.ParseIP(ip))
}
type cidrMatcher struct {
ipNet *net.IPNet
}
// CIDRMatcher creates a Matcher for a specific CIDR notation IP address.
func CIDRMatcher(inet *net.IPNet) Matcher {
return &cidrMatcher{
ipNet: inet,
}
}
func (m *cidrMatcher) Match(ip string) bool {
if m == nil || m.ipNet == nil {
return false
}
return m.ipNet.Contains(net.ParseIP(ip))
}
type domainMatcher struct {
pattern string
glob glob.Glob
}
// DomainMatcher creates a Matcher for a specific domain pattern,
// the pattern can be a plain domain such as 'example.com',
// a wildcard such as '*.exmaple.com' or a special wildcard '.example.com'.
func DomainMatcher(pattern string) Matcher {
p := pattern
if strings.HasPrefix(pattern, ".") {
p = pattern[1:] // trim the prefix '.'
pattern = "*" + p
}
return &domainMatcher{
pattern: p,
glob: glob.MustCompile(pattern),
}
}
func (m *domainMatcher) Match(domain string) bool {
if m == nil || m.glob == nil {
return false
}
if domain == m.pattern {
return true
}
return m.glob.Match(domain)
}

View File

@ -1,144 +0,0 @@
package dialer
import (
"context"
"fmt"
"net"
"syscall"
"time"
"github.com/go-gost/gost/v3/pkg/logger"
)
var (
DefaultNetDialer = &NetDialer{
Timeout: 30 * time.Second,
}
)
type NetDialer struct {
Interface string
Timeout time.Duration
DialFunc func(ctx context.Context, network, addr string) (net.Conn, error)
Logger logger.Logger
}
func (d *NetDialer) Dial(ctx context.Context, network, addr string) (net.Conn, error) {
if d == nil {
d = DefaultNetDialer
}
log := d.Logger
if log == nil {
log = logger.Default()
}
ifceName, ifAddr, err := parseInterfaceAddr(d.Interface, network)
if err != nil {
return nil, err
}
if d.DialFunc != nil {
return d.DialFunc(ctx, network, addr)
}
logger.Default().Infof("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)
}
return net.ListenUDP(network, laddr)
}
case "tcp", "tcp4", "tcp6":
default:
return nil, fmt.Errorf("dial: unsupported network %s", network)
}
netd := net.Dialer{
Timeout: d.Timeout,
LocalAddr: ifAddr,
Control: func(network, address string, c syscall.RawConn) error {
var cerr error
err := c.Control(func(fd uintptr) {
cerr = bindDevice(fd, ifceName)
})
if err != nil {
return err
}
if cerr != nil {
return cerr
}
return nil
},
}
return netd.DialContext(ctx, network, addr)
}
func parseInterfaceAddr(ifceName, network string) (ifce string, addr net.Addr, err error) {
if ifceName == "" {
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
}
ip = addrs[0].(*net.IPNet).IP
ifce = ifceName
} else {
ifce, err = findInterfaceByIP(ip)
if err != nil {
return
}
}
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
}

View File

@ -1,14 +0,0 @@
package dialer
import (
"golang.org/x/sys/unix"
)
func bindDevice(fd uintptr, ifceName string) error {
// unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
// unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
if ifceName == "" {
return nil
}
return unix.BindToDevice(int(fd), ifceName)
}

View File

@ -1,7 +0,0 @@
//go:build !linux
package dialer
func bindDevice(fd uintptr, ifceName string) error {
return nil
}

View File

@ -1,126 +0,0 @@
package relay
import (
"net"
"github.com/go-gost/gost/v3/pkg/bypass"
"github.com/go-gost/gost/v3/pkg/common/bufpool"
"github.com/go-gost/gost/v3/pkg/logger"
)
type UDPRelay struct {
pc1 net.PacketConn
pc2 net.PacketConn
bypass bypass.Bypass
bufferSize int
logger logger.Logger
}
func NewUDPRelay(pc1, pc2 net.PacketConn) *UDPRelay {
return &UDPRelay{
pc1: pc1,
pc2: pc2,
}
}
func (r *UDPRelay) WithBypass(bp bypass.Bypass) *UDPRelay {
r.bypass = bp
return r
}
func (r *UDPRelay) WithLogger(logger logger.Logger) *UDPRelay {
r.logger = logger
return r
}
func (r *UDPRelay) SetBufferSize(n int) {
r.bufferSize = n
}
func (r *UDPRelay) Run() (err error) {
bufSize := r.bufferSize
if bufSize <= 0 {
bufSize = 1024
}
errc := make(chan error, 2)
go func() {
for {
err := func() error {
b := bufpool.Get(bufSize)
defer bufpool.Put(b)
n, raddr, err := r.pc1.ReadFrom(*b)
if err != nil {
return err
}
if r.bypass != nil && r.bypass.Contains(raddr.String()) {
if r.logger != nil {
r.logger.Warn("bypass: ", raddr)
}
return nil
}
if _, err := r.pc2.WriteTo((*b)[:n], raddr); err != nil {
return err
}
if r.logger != nil {
r.logger.Debugf("%s >>> %s data: %d",
r.pc2.LocalAddr(), raddr, n)
}
return nil
}()
if err != nil {
errc <- err
return
}
}
}()
go func() {
for {
err := func() error {
b := bufpool.Get(bufSize)
defer bufpool.Put(b)
n, raddr, err := r.pc2.ReadFrom(*b)
if err != nil {
return err
}
if r.bypass != nil && r.bypass.Contains(raddr.String()) {
if r.logger != nil {
r.logger.Warn("bypass: ", raddr)
}
return nil
}
if _, err := r.pc1.WriteTo((*b)[:n], raddr); err != nil {
return err
}
if r.logger != nil {
r.logger.Debugf("%s <<< %s data: %d",
r.pc2.LocalAddr(), raddr, n)
}
return nil
}()
if err != nil {
errc <- err
return
}
}
}()
return <-errc
}

View File

@ -1,50 +0,0 @@
package net
import (
"bufio"
"io"
"net"
"github.com/go-gost/gost/v3/pkg/common/bufpool"
)
func Transport(rw1, rw2 io.ReadWriter) error {
errc := make(chan error, 1)
go func() {
errc <- copyBuffer(rw1, rw2)
}()
go func() {
errc <- copyBuffer(rw2, rw1)
}()
err := <-errc
if err != nil && err == io.EOF {
err = nil
}
return err
}
func copyBuffer(dst io.Writer, src io.Reader) error {
buf := bufpool.Get(16 * 1024)
defer bufpool.Put(buf)
_, err := io.CopyBuffer(dst, src, *buf)
return err
}
type bufferReaderConn struct {
net.Conn
br *bufio.Reader
}
func NewBufferReaderConn(conn net.Conn, br *bufio.Reader) net.Conn {
return &bufferReaderConn{
Conn: conn,
br: br,
}
}
func (c *bufferReaderConn) Read(b []byte) (int, error) {
return c.br.Read(b)
}

View File

@ -1,41 +0,0 @@
package net
import (
"io"
"net"
"syscall"
)
type UDPConn interface {
net.PacketConn
io.Reader
io.Writer
readUDP
writeUDP
setBuffer
syscallConn
remoteAddr
}
type setBuffer interface {
SetReadBuffer(bytes int) error
SetWriteBuffer(bytes int) error
}
type readUDP interface {
ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error)
ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error)
}
type writeUDP interface {
WriteToUDP(b []byte, addr *net.UDPAddr) (int, error)
WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error)
}
type syscallConn interface {
SyscallConn() (syscall.RawConn, error)
}
type remoteAddr interface {
RemoteAddr() net.Addr
}

View File

@ -1,88 +0,0 @@
package resolver
import (
"fmt"
"sync"
"time"
"github.com/go-gost/gost/v3/pkg/logger"
"github.com/miekg/dns"
)
type CacheKey string
// NewCacheKey generates resolver cache key from question of dns query.
func NewCacheKey(q *dns.Question) CacheKey {
if q == nil {
return ""
}
key := fmt.Sprintf("%s%s.%s", q.Name, dns.Class(q.Qclass).String(), dns.Type(q.Qtype).String())
return CacheKey(key)
}
type cacheItem struct {
msg *dns.Msg
ts time.Time
ttl time.Duration
}
type Cache struct {
m sync.Map
logger logger.Logger
}
func NewCache() *Cache {
return &Cache{}
}
func (c *Cache) WithLogger(logger logger.Logger) *Cache {
c.logger = logger
return c
}
func (c *Cache) Load(key CacheKey) *dns.Msg {
v, ok := c.m.Load(key)
if !ok {
return nil
}
item, ok := v.(*cacheItem)
if !ok {
return nil
}
if time.Since(item.ts) > item.ttl {
c.m.Delete(key)
return nil
}
c.logger.Debugf("hit resolver cache: %s", key)
return item.msg.Copy()
}
func (c *Cache) Store(key CacheKey, mr *dns.Msg, ttl time.Duration) {
if key == "" || mr == nil || ttl < 0 {
return
}
if ttl == 0 {
for _, answer := range mr.Answer {
v := time.Duration(answer.Header().Ttl) * time.Second
if ttl == 0 || ttl > v {
ttl = v
}
}
}
if ttl == 0 {
ttl = 30 * time.Second
}
c.m.Store(key, &cacheItem{
msg: mr.Copy(),
ts: time.Now(),
ttl: ttl,
})
c.logger.Debugf("resolver cache store: %s, ttl: %v", key, ttl)
}

View File

@ -1,30 +0,0 @@
package resolver
import (
"net"
"github.com/miekg/dns"
)
func AddSubnetOpt(m *dns.Msg, ip net.IP) {
if m == nil || ip == nil {
return
}
opt := new(dns.OPT)
opt.Hdr.Name = "."
opt.Hdr.Rrtype = dns.TypeOPT
e := new(dns.EDNS0_SUBNET)
e.Code = dns.EDNS0SUBNET
if ip := ip.To4(); ip != nil {
e.Family = 1
e.SourceNetmask = 24
e.Address = ip
} else {
e.Family = 2
e.SourceNetmask = 128
e.Address = ip.To16()
}
opt.Option = append(opt.Option, e)
m.Extra = append(m.Extra, opt)
}

View File

@ -1,178 +0,0 @@
package tls
import (
"crypto/tls"
"crypto/x509"
"errors"
"io/ioutil"
"net"
"time"
)
var (
// DefaultConfig is a default TLS config for global use.
DefaultConfig *tls.Config
)
// LoadServerConfig loads the certificate from cert & key files and optional client CA file.
func LoadServerConfig(certFile, keyFile, caFile string) (*tls.Config, error) {
if certFile == "" && keyFile == "" {
return DefaultConfig.Clone(), nil
}
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, err
}
cfg := &tls.Config{Certificates: []tls.Certificate{cert}}
pool, err := loadCA(caFile)
if err != nil {
return nil, err
}
if pool != nil {
cfg.ClientCAs = pool
cfg.ClientAuth = tls.RequireAndVerifyClientCert
}
return cfg, nil
}
// LoadClientConfig loads the certificate from cert & key files and optional CA file.
func LoadClientConfig(certFile, keyFile, caFile string, verify bool, serverName string) (*tls.Config, error) {
var cfg *tls.Config
if certFile == "" && keyFile == "" {
cfg = &tls.Config{}
} else {
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return nil, err
}
cfg = &tls.Config{
Certificates: []tls.Certificate{cert},
}
}
rootCAs, err := loadCA(caFile)
if err != nil {
return nil, err
}
cfg.RootCAs = rootCAs
cfg.ServerName = serverName
cfg.InsecureSkipVerify = !verify
// If the root ca is given, but skip verify, we verify the certificate manually.
if cfg.RootCAs != nil && !verify {
cfg.VerifyConnection = func(state tls.ConnectionState) error {
opts := x509.VerifyOptions{
Roots: cfg.RootCAs,
CurrentTime: time.Now(),
DNSName: "",
Intermediates: x509.NewCertPool(),
}
certs := state.PeerCertificates
for i, cert := range certs {
if i == 0 {
continue
}
opts.Intermediates.AddCert(cert)
}
_, err := certs[0].Verify(opts)
return err
}
}
return cfg, nil
}
func loadCA(caFile string) (cp *x509.CertPool, err error) {
if caFile == "" {
return
}
cp = x509.NewCertPool()
data, err := ioutil.ReadFile(caFile)
if err != nil {
return nil, err
}
if !cp.AppendCertsFromPEM(data) {
return nil, errors.New("AppendCertsFromPEM failed")
}
return
}
// Wrap a net.Conn into a client tls connection, performing any
// additional verification as needed.
//
// As of go 1.3, crypto/tls only supports either doing no certificate
// verification, or doing full verification including of the peer's
// DNS name. For consul, we want to validate that the certificate is
// signed by a known CA, but because consul doesn't use DNS names for
// node names, we don't verify the certificate DNS names. Since go 1.3
// no longer supports this mode of operation, we have to do it
// manually.
//
// This code is taken from consul:
// https://github.com/hashicorp/consul/blob/master/tlsutil/config.go
func WrapTLSClient(conn net.Conn, tlsConfig *tls.Config, timeout time.Duration) (net.Conn, error) {
var err error
var tlsConn *tls.Conn
if timeout > 0 {
conn.SetDeadline(time.Now().Add(timeout))
defer conn.SetDeadline(time.Time{})
}
tlsConn = tls.Client(conn, tlsConfig)
// Otherwise perform handshake, but don't verify the domain
//
// The following is lightly-modified from the doFullHandshake
// method in https://golang.org/src/crypto/tls/handshake_client.go
if err = tlsConn.Handshake(); err != nil {
tlsConn.Close()
return nil, err
}
// We can do this in `tls.Config.VerifyConnection`, which effective for
// other TLS protocols such as WebSocket. See `route.go:parseChainNode`
/*
// If crypto/tls is doing verification, there's no need to do our own.
if tlsConfig.InsecureSkipVerify == false {
return tlsConn, nil
}
// Similarly if we use host's CA, we can do full handshake
if tlsConfig.RootCAs == nil {
return tlsConn, nil
}
opts := x509.VerifyOptions{
Roots: tlsConfig.RootCAs,
CurrentTime: time.Now(),
DNSName: "",
Intermediates: x509.NewCertPool(),
}
certs := tlsConn.ConnectionState().PeerCertificates
for i, cert := range certs {
if i == 0 {
continue
}
opts.Intermediates.AddCert(cert)
}
_, err = certs[0].Verify(opts)
if err != nil {
tlsConn.Close()
return nil, err
}
*/
return tlsConn, err
}

View File

@ -1,102 +0,0 @@
package udp
import (
"errors"
"net"
"sync"
"sync/atomic"
"github.com/go-gost/gost/v3/pkg/common/bufpool"
)
// 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
}
func NewConn(c net.PacketConn, localAddr, remoteAddr net.Addr, queueSize int) *Conn {
return &Conn{
PacketConn: c,
localAddr: localAddr,
remoteAddr: remoteAddr,
rc: make(chan []byte, queueSize),
closed: make(chan struct{}),
}
}
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) {
return c.WriteTo(b, c.remoteAddr)
}
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")
}
}

View File

@ -1,120 +0,0 @@
package udp
import (
"net"
"sync"
"time"
"github.com/go-gost/gost/v3/pkg/common/bufpool"
"github.com/go-gost/gost/v3/pkg/logger"
)
type listener struct {
addr net.Addr
conn net.PacketConn
cqueue chan net.Conn
readQueueSize int
readBufferSize int
connPool *ConnPool
mux sync.Mutex
closed chan struct{}
errChan chan error
logger logger.Logger
}
func NewListener(conn net.PacketConn, addr net.Addr, backlog, dataQueueSize, dataBufferSize int, ttl time.Duration, logger logger.Logger) net.Listener {
ln := &listener{
conn: conn,
addr: addr,
cqueue: make(chan net.Conn, backlog),
connPool: NewConnPool(ttl).WithLogger(logger),
readQueueSize: dataQueueSize,
readBufferSize: dataBufferSize,
closed: make(chan struct{}),
errChan: make(chan error, 1),
logger: 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.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.logger.Warn("data discarded: ", err)
}
}
}
func (ln *listener) Addr() net.Addr {
return ln.addr
}
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.readQueueSize)
select {
case ln.cqueue <- c:
ln.connPool.Set(raddr.String(), c)
return c
default:
c.Close()
ln.logger.Warnf("connection queue is full, client %s discarded", raddr)
return nil
}
}

View File

@ -1,100 +0,0 @@
package udp
import (
"sync"
"time"
"github.com/go-gost/gost/v3/pkg/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) {
v, ok := p.m.Load(key)
if ok {
c, ok = v.(*Conn)
}
return
}
func (p *ConnPool) Set(key any, c *Conn) {
p.m.Store(key, c)
}
func (p *ConnPool) Delete(key any) {
p.m.Delete(key)
}
func (p *ConnPool) Close() {
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
}
}
}

View File

@ -1,15 +0,0 @@
package connector
import (
"context"
"errors"
"net"
)
var (
ErrBindUnsupported = errors.New("bind unsupported")
)
type Binder interface {
Bind(ctx context.Context, conn net.Conn, network, address string, opts ...BindOption) (net.Listener, error)
}

View File

@ -1,18 +0,0 @@
package connector
import (
"context"
"net"
"github.com/go-gost/gost/v3/pkg/metadata"
)
// Connector is responsible for connecting to the destination address.
type Connector interface {
Init(metadata.Metadata) error
Connect(ctx context.Context, conn net.Conn, network, address string, opts ...ConnectOption) (net.Conn, error)
}
type Handshaker interface {
Handshake(ctx context.Context, conn net.Conn) (net.Conn, error)
}

View File

@ -1,45 +0,0 @@
package forward
import (
"context"
"net"
"github.com/go-gost/gost/v3/pkg/connector"
md "github.com/go-gost/gost/v3/pkg/metadata"
"github.com/go-gost/gost/v3/pkg/registry"
)
func init() {
registry.ConnectorRegistry().Register("forward", NewConnector)
}
type forwardConnector struct {
options connector.Options
}
func NewConnector(opts ...connector.Option) connector.Connector {
options := connector.Options{}
for _, opt := range opts {
opt(&options)
}
return &forwardConnector{
options: options,
}
}
func (c *forwardConnector) Init(md md.Metadata) (err error) {
return nil
}
func (c *forwardConnector) Connect(ctx context.Context, conn net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
log := c.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"network": network,
"address": address,
})
log.Infof("connect %s/%s", address, network)
return conn, nil
}

View File

@ -1,129 +0,0 @@
package http
import (
"bufio"
"context"
"encoding/base64"
"fmt"
"net"
"net/http"
"net/http/httputil"
"net/url"
"time"
"github.com/go-gost/gost/v3/pkg/connector"
"github.com/go-gost/gost/v3/pkg/internal/util/socks"
"github.com/go-gost/gost/v3/pkg/logger"
md "github.com/go-gost/gost/v3/pkg/metadata"
"github.com/go-gost/gost/v3/pkg/registry"
)
func init() {
registry.ConnectorRegistry().Register("http", NewConnector)
}
type httpConnector struct {
md metadata
options connector.Options
}
func NewConnector(opts ...connector.Option) connector.Connector {
options := connector.Options{}
for _, opt := range opts {
opt(&options)
}
return &httpConnector{
options: options,
}
}
func (c *httpConnector) Init(md md.Metadata) (err error) {
return c.parseMetadata(md)
}
func (c *httpConnector) Connect(ctx context.Context, conn net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
log := c.options.Logger.WithFields(map[string]any{
"local": conn.LocalAddr().String(),
"remote": conn.RemoteAddr().String(),
"network": network,
"address": address,
})
log.Infof("connect %s/%s", address, network)
req := &http.Request{
Method: http.MethodConnect,
URL: &url.URL{Host: address},
Host: address,
ProtoMajor: 1,
ProtoMinor: 1,
Header: c.md.header,
}
if req.Header == nil {
req.Header = http.Header{}
}
req.Header.Set("Proxy-Connection", "keep-alive")
if user := c.options.Auth; user != nil {
u := user.Username()
p, _ := user.Password()
req.Header.Set("Proxy-Authorization",
"Basic "+base64.StdEncoding.EncodeToString([]byte(u+":"+p)))
}
switch network {
case "tcp", "tcp4", "tcp6":
if _, ok := conn.(net.PacketConn); ok {
err := fmt.Errorf("tcp over udp is unsupported")
log.Error(err)
return nil, err
}
case "udp", "udp4", "udp6":
req.Header.Set("X-Gost-Protocol", "udp")
default:
err := fmt.Errorf("network %s is unsupported", network)
log.Error(err)
return nil, err
}
if log.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpRequest(req, false)
log.Debug(string(dump))
}
if c.md.connectTimeout > 0 {
conn.SetDeadline(time.Now().Add(c.md.connectTimeout))
defer conn.SetDeadline(time.Time{})
}
req = req.WithContext(ctx)
if err := req.Write(conn); err != nil {
return nil, err
}
resp, err := http.ReadResponse(bufio.NewReader(conn), req)
if err != nil {
return nil, err
}
// NOTE: the server may return `Transfer-Encoding: chunked` header,
// then the Content-Length of response will be unknown (-1),
// in this case, close body will be blocked, so we leave it untouched.
// defer resp.Body.Close()
if log.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpResponse(resp, false)
log.Debug(string(dump))
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%s", resp.Status)
}
if network == "udp" {
addr, _ := net.ResolveUDPAddr(network, address)
return socks.UDPTunClientConn(conn, addr), nil
}
return conn, nil
}

View File

@ -1,32 +0,0 @@
package http
import (
"net/http"
"time"
mdata "github.com/go-gost/gost/v3/pkg/metadata"
)
type metadata struct {
connectTimeout time.Duration
header http.Header
}
func (c *httpConnector) parseMetadata(md mdata.Metadata) (err error) {
const (
connectTimeout = "timeout"
header = "header"
)
c.md.connectTimeout = mdata.GetDuration(md, connectTimeout)
if mm := mdata.GetStringMapString(md, header); len(mm) > 0 {
hd := http.Header{}
for k, v := range mm {
hd.Add(k, v)
}
c.md.header = hd
}
return
}

View File

@ -1,80 +0,0 @@
package connector
import (
"crypto/tls"
"net/url"
"time"
"github.com/go-gost/gost/v3/pkg/logger"
)
type Options struct {
Auth *url.Userinfo
TLSConfig *tls.Config
Logger logger.Logger
}
type Option func(opts *Options)
func AuthOption(auth *url.Userinfo) Option {
return func(opts *Options) {
opts.Auth = auth
}
}
func TLSConfigOption(tlsConfig *tls.Config) Option {
return func(opts *Options) {
opts.TLSConfig = tlsConfig
}
}
func LoggerOption(logger logger.Logger) Option {
return func(opts *Options) {
opts.Logger = logger
}
}
type ConnectOptions struct {
}
type ConnectOption func(opts *ConnectOptions)
type BindOptions struct {
Mux bool
Backlog int
UDPDataQueueSize int
UDPDataBufferSize int
UDPConnTTL time.Duration
}
type BindOption func(opts *BindOptions)
func MuxBindOption(mux bool) BindOption {
return func(opts *BindOptions) {
opts.Mux = mux
}
}
func BacklogBindOption(backlog int) BindOption {
return func(opts *BindOptions) {
opts.Backlog = backlog
}
}
func UDPDataQueueSizeBindOption(size int) BindOption {
return func(opts *BindOptions) {
opts.UDPDataQueueSize = size
}
}
func UDPDataBufferSizeBindOption(size int) BindOption {
return func(opts *BindOptions) {
opts.UDPDataBufferSize = size
}
}
func UDPConnTTLBindOption(ttl time.Duration) BindOption {
return func(opts *BindOptions) {
opts.UDPConnTTL = ttl
}
}

View File

@ -1,123 +0,0 @@
package v4
import (
"context"
"errors"
"fmt"
"net"
"strconv"
"time"
"github.com/go-gost/gosocks4"
"github.com/go-gost/gost/v3/pkg/connector"
md "github.com/go-gost/gost/v3/pkg/metadata"
"github.com/go-gost/gost/v3/pkg/registry"
)
func init() {
registry.ConnectorRegistry().Register("socks4", NewConnector)
registry.ConnectorRegistry().Register("socks4a", NewConnector)
}
type socks4Connector struct {
md metadata
options connector.Options
}
func NewConnector(opts ...connector.Option) connector.Connector {
options := connector.Options{}
for _, opt := range opts {
opt(&options)
}
return &socks4Connector{
options: options,
}
}
func (c *socks4Connector) Init(md md.Metadata) (err error) {
return c.parseMetadata(md)
}
func (c *socks4Connector) Connect(ctx context.Context, conn net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
log := c.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"network": network,
"address": address,
})
log.Infof("connect %s/%s", address, network)
switch network {
case "tcp", "tcp4", "tcp6":
if _, ok := conn.(net.PacketConn); ok {
err := fmt.Errorf("tcp over udp is unsupported")
log.Error(err)
return nil, err
}
default:
err := fmt.Errorf("network %s is unsupported", network)
log.Error(err)
return nil, err
}
var addr *gosocks4.Addr
if c.md.disable4a {
taddr, err := net.ResolveTCPAddr("tcp4", address)
if err != nil {
log.Error("resolve: ", err)
return nil, err
}
if len(taddr.IP) == 0 {
taddr.IP = net.IPv4zero
}
addr = &gosocks4.Addr{
Type: gosocks4.AddrIPv4,
Host: taddr.IP.String(),
Port: uint16(taddr.Port),
}
} else {
host, port, err := net.SplitHostPort(address)
if err != nil {
return nil, err
}
p, _ := strconv.Atoi(port)
addr = &gosocks4.Addr{
Type: gosocks4.AddrDomain,
Host: host,
Port: uint16(p),
}
}
if c.md.connectTimeout > 0 {
conn.SetDeadline(time.Now().Add(c.md.connectTimeout))
defer conn.SetDeadline(time.Time{})
}
var userid []byte
if c.options.Auth != nil {
userid = []byte(c.options.Auth.Username())
}
req := gosocks4.NewRequest(gosocks4.CmdConnect, addr, userid)
if err := req.Write(conn); err != nil {
log.Error(err)
return nil, err
}
log.Debug(req)
reply, err := gosocks4.ReadReply(conn)
if err != nil {
log.Error(err)
return nil, err
}
log.Debug(reply)
if reply.Code != gosocks4.Granted {
err = errors.New("host unreachable")
log.Error(err)
return nil, err
}
return conn, nil
}

View File

@ -1,24 +0,0 @@
package v4
import (
"time"
mdata "github.com/go-gost/gost/v3/pkg/metadata"
)
type metadata struct {
connectTimeout time.Duration
disable4a bool
}
func (c *socks4Connector) parseMetadata(md mdata.Metadata) (err error) {
const (
connectTimeout = "timeout"
disable4a = "disable4a"
)
c.md.connectTimeout = mdata.GetDuration(md, connectTimeout)
c.md.disable4a = mdata.GetBool(md, disable4a)
return
}

View File

@ -1,130 +0,0 @@
package v5
import (
"context"
"fmt"
"net"
"github.com/go-gost/gosocks5"
"github.com/go-gost/gost/v3/pkg/common/util/udp"
"github.com/go-gost/gost/v3/pkg/connector"
"github.com/go-gost/gost/v3/pkg/internal/util/mux"
"github.com/go-gost/gost/v3/pkg/internal/util/socks"
"github.com/go-gost/gost/v3/pkg/logger"
)
// Bind implements connector.Binder.
func (c *socks5Connector) Bind(ctx context.Context, conn net.Conn, network, address string, opts ...connector.BindOption) (net.Listener, error) {
log := c.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"network": network,
"address": address,
})
log.Infof("bind on %s/%s", address, network)
options := connector.BindOptions{}
for _, opt := range opts {
opt(&options)
}
switch network {
case "tcp", "tcp4", "tcp6":
if options.Mux {
return c.muxBindTCP(ctx, conn, network, address, log)
}
return c.bindTCP(ctx, conn, network, address, log)
case "udp", "udp4", "udp6":
return c.bindUDP(ctx, conn, network, address, &options, log)
default:
err := fmt.Errorf("network %s is unsupported", network)
log.Error(err)
return nil, err
}
}
func (c *socks5Connector) bindTCP(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) (net.Listener, error) {
laddr, err := c.bind(conn, gosocks5.CmdBind, network, address, log)
if err != nil {
return nil, err
}
return &tcpListener{
addr: laddr,
conn: conn,
logger: log,
}, nil
}
func (c *socks5Connector) muxBindTCP(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) (net.Listener, error) {
laddr, err := c.bind(conn, socks.CmdMuxBind, network, address, log)
if err != nil {
return nil, err
}
session, err := mux.ServerSession(conn)
if err != nil {
return nil, err
}
return &tcpMuxListener{
addr: laddr,
session: session,
logger: log,
}, nil
}
func (c *socks5Connector) bindUDP(ctx context.Context, conn net.Conn, network, address string, opts *connector.BindOptions, log logger.Logger) (net.Listener, error) {
laddr, err := c.bind(conn, socks.CmdUDPTun, network, address, log)
if err != nil {
return nil, err
}
ln := udp.NewListener(
socks.UDPTunClientPacketConn(conn),
laddr,
opts.Backlog,
opts.UDPDataQueueSize, opts.UDPDataBufferSize,
opts.UDPConnTTL,
log)
return ln, nil
}
func (l *socks5Connector) bind(conn net.Conn, cmd uint8, network, address string, log logger.Logger) (net.Addr, error) {
addr := gosocks5.Addr{}
addr.ParseFrom(address)
req := gosocks5.NewRequest(cmd, &addr)
if err := req.Write(conn); err != nil {
return nil, err
}
log.Debug(req)
// first reply, bind status
reply, err := gosocks5.ReadReply(conn)
if err != nil {
return nil, err
}
log.Debug(reply)
if reply.Rep != gosocks5.Succeeded {
return nil, fmt.Errorf("bind on %s/%s failed", address, network)
}
var baddr net.Addr
switch network {
case "tcp", "tcp4", "tcp6":
baddr, err = net.ResolveTCPAddr(network, reply.Addr.String())
case "udp", "udp4", "udp6":
baddr, err = net.ResolveUDPAddr(network, reply.Addr.String())
default:
err = fmt.Errorf("unknown network %s", network)
}
if err != nil {
return nil, err
}
log.Debugf("bind on %s/%s OK", baddr, baddr.Network())
return baddr, nil
}

View File

@ -1,17 +0,0 @@
package v5
import "net"
type bindConn struct {
net.Conn
localAddr net.Addr
remoteAddr net.Addr
}
func (c *bindConn) LocalAddr() net.Addr {
return c.localAddr
}
func (c *bindConn) RemoteAddr() net.Addr {
return c.remoteAddr
}

View File

@ -1,173 +0,0 @@
package v5
import (
"context"
"crypto/tls"
"errors"
"fmt"
"net"
"time"
"github.com/go-gost/gosocks5"
"github.com/go-gost/gost/v3/pkg/connector"
"github.com/go-gost/gost/v3/pkg/internal/util/socks"
"github.com/go-gost/gost/v3/pkg/logger"
md "github.com/go-gost/gost/v3/pkg/metadata"
"github.com/go-gost/gost/v3/pkg/registry"
)
func init() {
registry.ConnectorRegistry().Register("socks5", NewConnector)
registry.ConnectorRegistry().Register("socks", NewConnector)
}
type socks5Connector struct {
selector gosocks5.Selector
md metadata
options connector.Options
}
func NewConnector(opts ...connector.Option) connector.Connector {
options := connector.Options{}
for _, opt := range opts {
opt(&options)
}
return &socks5Connector{
options: options,
}
}
func (c *socks5Connector) Init(md md.Metadata) (err error) {
if err = c.parseMetadata(md); err != nil {
return
}
selector := &clientSelector{
methods: []uint8{
gosocks5.MethodNoAuth,
gosocks5.MethodUserPass,
},
User: c.options.Auth,
TLSConfig: c.options.TLSConfig,
logger: c.options.Logger,
}
if !c.md.noTLS {
selector.methods = append(selector.methods, socks.MethodTLS)
if selector.TLSConfig == nil {
selector.TLSConfig = &tls.Config{
InsecureSkipVerify: true,
}
}
}
c.selector = selector
return
}
// Handshake implements connector.Handshaker.
func (c *socks5Connector) Handshake(ctx context.Context, conn net.Conn) (net.Conn, error) {
log := c.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
})
if c.md.connectTimeout > 0 {
conn.SetDeadline(time.Now().Add(c.md.connectTimeout))
defer conn.SetDeadline(time.Time{})
}
cc := gosocks5.ClientConn(conn, c.selector)
if err := cc.Handleshake(); err != nil {
log.Error(err)
return nil, err
}
return cc, nil
}
func (c *socks5Connector) Connect(ctx context.Context, conn net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
log := c.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"network": network,
"address": address,
})
log.Infof("connect %s/%s", address, network)
if c.md.connectTimeout > 0 {
conn.SetDeadline(time.Now().Add(c.md.connectTimeout))
defer conn.SetDeadline(time.Time{})
}
switch network {
case "udp", "udp4", "udp6":
return c.connectUDP(ctx, conn, network, address, log)
case "tcp", "tcp4", "tcp6":
if _, ok := conn.(net.PacketConn); ok {
err := fmt.Errorf("tcp over udp is unsupported")
log.Error(err)
return nil, err
}
default:
err := fmt.Errorf("network %s is unsupported", network)
log.Error(err)
return nil, err
}
addr := gosocks5.Addr{}
if err := addr.ParseFrom(address); err != nil {
log.Error(err)
return nil, err
}
req := gosocks5.NewRequest(gosocks5.CmdConnect, &addr)
if err := req.Write(conn); err != nil {
log.Error(err)
return nil, err
}
log.Debug(req)
reply, err := gosocks5.ReadReply(conn)
if err != nil {
log.Error(err)
return nil, err
}
log.Debug(reply)
if reply.Rep != gosocks5.Succeeded {
err = errors.New("host unreachable")
log.Error(err)
return nil, err
}
return conn, nil
}
func (c *socks5Connector) connectUDP(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) (net.Conn, error) {
addr, err := net.ResolveUDPAddr(network, address)
if err != nil {
log.Error(err)
return nil, err
}
req := gosocks5.NewRequest(socks.CmdUDPTun, nil)
if err := req.Write(conn); err != nil {
log.Error(err)
return nil, err
}
log.Debug(req)
reply, err := gosocks5.ReadReply(conn)
if err != nil {
log.Error(err)
return nil, err
}
log.Debug(reply)
if reply.Rep != gosocks5.Succeeded {
return nil, errors.New("get socks5 UDP tunnel failure")
}
return socks.UDPTunClientConn(conn, addr), nil
}

View File

@ -1,102 +0,0 @@
package v5
import (
"fmt"
"net"
"github.com/go-gost/gosocks5"
"github.com/go-gost/gost/v3/pkg/internal/util/mux"
"github.com/go-gost/gost/v3/pkg/logger"
)
type tcpListener struct {
addr net.Addr
conn net.Conn
logger logger.Logger
}
func (p *tcpListener) Accept() (net.Conn, error) {
// second reply, peer connected
rep, err := gosocks5.ReadReply(p.conn)
if err != nil {
return nil, err
}
p.logger.Debug(rep)
if rep.Rep != gosocks5.Succeeded {
return nil, fmt.Errorf("peer connect failed")
}
raddr, err := net.ResolveTCPAddr("tcp", rep.Addr.String())
if err != nil {
return nil, err
}
return &bindConn{
Conn: p.conn,
localAddr: p.addr,
remoteAddr: raddr,
}, nil
}
func (p *tcpListener) Addr() net.Addr {
return p.addr
}
func (p *tcpListener) Close() error {
return p.conn.Close()
}
type tcpMuxListener struct {
addr net.Addr
session *mux.Session
logger logger.Logger
}
func (p *tcpMuxListener) Accept() (net.Conn, error) {
cc, err := p.session.Accept()
if err != nil {
return nil, err
}
conn, err := p.getPeerConn(cc)
if err != nil {
cc.Close()
return nil, err
}
return conn, nil
}
func (p *tcpMuxListener) getPeerConn(conn net.Conn) (net.Conn, error) {
// second reply, peer connected
rep, err := gosocks5.ReadReply(conn)
if err != nil {
return nil, err
}
p.logger.Debug(rep)
if rep.Rep != gosocks5.Succeeded {
err = fmt.Errorf("peer connect failed")
return nil, err
}
raddr, err := net.ResolveTCPAddr("tcp", rep.Addr.String())
if err != nil {
return nil, err
}
return &bindConn{
Conn: conn,
localAddr: p.addr,
remoteAddr: raddr,
}, nil
}
func (p *tcpMuxListener) Addr() net.Addr {
return p.addr
}
func (p *tcpMuxListener) Close() error {
return p.session.Close()
}

View File

@ -1,24 +0,0 @@
package v5
import (
"time"
mdata "github.com/go-gost/gost/v3/pkg/metadata"
)
type metadata struct {
connectTimeout time.Duration
noTLS bool
}
func (c *socks5Connector) parseMetadata(md mdata.Metadata) (err error) {
const (
connectTimeout = "timeout"
noTLS = "notls"
)
c.md.connectTimeout = mdata.GetDuration(md, connectTimeout)
c.md.noTLS = mdata.GetBool(md, noTLS)
return
}

View File

@ -1,73 +0,0 @@
package v5
import (
"crypto/tls"
"net"
"net/url"
"github.com/go-gost/gosocks5"
"github.com/go-gost/gost/v3/pkg/internal/util/socks"
"github.com/go-gost/gost/v3/pkg/logger"
)
type clientSelector struct {
methods []uint8
User *url.Userinfo
TLSConfig *tls.Config
logger logger.Logger
}
func (s *clientSelector) Methods() []uint8 {
s.logger.Debug("methods: ", s.methods)
return s.methods
}
func (s *clientSelector) AddMethod(methods ...uint8) {
s.methods = append(s.methods, methods...)
}
func (s *clientSelector) Select(methods ...uint8) (method uint8) {
return
}
func (s *clientSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) {
s.logger.Debug("method selected: ", method)
switch method {
case socks.MethodTLS:
conn = tls.Client(conn, s.TLSConfig)
case gosocks5.MethodUserPass, socks.MethodTLSAuth:
if method == socks.MethodTLSAuth {
conn = tls.Client(conn, s.TLSConfig)
}
var username, password string
if s.User != nil {
username = s.User.Username()
password, _ = s.User.Password()
}
req := gosocks5.NewUserPassRequest(gosocks5.UserPassVer, username, password)
if err := req.Write(conn); err != nil {
s.logger.Error(err)
return nil, err
}
s.logger.Debug(req)
resp, err := gosocks5.ReadUserPassResponse(conn)
if err != nil {
s.logger.Error(err)
return nil, err
}
s.logger.Debug(resp)
if resp.Status != gosocks5.Succeeded {
return nil, gosocks5.ErrAuthFailure
}
case gosocks5.MethodNoAcceptable:
return nil, gosocks5.ErrBadMethod
}
return conn, nil
}

View File

@ -1,22 +0,0 @@
package dialer
import (
"context"
"net"
"github.com/go-gost/gost/v3/pkg/metadata"
)
// Transporter is responsible for dialing to the proxy server.
type Dialer interface {
Init(metadata.Metadata) error
Dial(ctx context.Context, addr string, opts ...DialOption) (net.Conn, error)
}
type Handshaker interface {
Handshake(ctx context.Context, conn net.Conn, opts ...HandshakeOption) (net.Conn, error)
}
type Multiplexer interface {
Multiplex() bool
}

View File

@ -1,66 +0,0 @@
package dialer
import (
"crypto/tls"
"net/url"
"github.com/go-gost/gost/v3/pkg/common/net/dialer"
"github.com/go-gost/gost/v3/pkg/logger"
)
type Options struct {
Auth *url.Userinfo
TLSConfig *tls.Config
Logger logger.Logger
}
type Option func(opts *Options)
func AuthOption(auth *url.Userinfo) Option {
return func(opts *Options) {
opts.Auth = auth
}
}
func TLSConfigOption(tlsConfig *tls.Config) Option {
return func(opts *Options) {
opts.TLSConfig = tlsConfig
}
}
func LoggerOption(logger logger.Logger) Option {
return func(opts *Options) {
opts.Logger = logger
}
}
type DialOptions struct {
Host string
NetDialer *dialer.NetDialer
}
type DialOption func(opts *DialOptions)
func HostDialOption(host string) DialOption {
return func(opts *DialOptions) {
opts.Host = host
}
}
func NetDialerDialOption(netd *dialer.NetDialer) DialOption {
return func(opts *DialOptions) {
opts.NetDialer = netd
}
}
type HandshakeOptions struct {
Addr string
}
type HandshakeOption func(opts *HandshakeOptions)
func AddrHandshakeOption(addr string) HandshakeOption {
return func(opts *HandshakeOptions) {
opts.Addr = addr
}
}

View File

@ -1,48 +0,0 @@
package tcp
import (
"context"
"net"
"github.com/go-gost/gost/v3/pkg/dialer"
"github.com/go-gost/gost/v3/pkg/logger"
md "github.com/go-gost/gost/v3/pkg/metadata"
"github.com/go-gost/gost/v3/pkg/registry"
)
func init() {
registry.DialerRegistry().Register("tcp", NewDialer)
}
type tcpDialer struct {
md metadata
logger logger.Logger
}
func NewDialer(opts ...dialer.Option) dialer.Dialer {
options := &dialer.Options{}
for _, opt := range opts {
opt(options)
}
return &tcpDialer{
logger: options.Logger,
}
}
func (d *tcpDialer) Init(md md.Metadata) (err error) {
return d.parseMetadata(md)
}
func (d *tcpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOption) (net.Conn, error) {
var options dialer.DialOptions
for _, opt := range opts {
opt(&options)
}
conn, err := options.NetDialer.Dial(ctx, "tcp", addr)
if err != nil {
d.logger.Error(err)
}
return conn, err
}

View File

@ -1,23 +0,0 @@
package tcp
import (
"time"
md "github.com/go-gost/gost/v3/pkg/metadata"
)
const (
dialTimeout = "dialTimeout"
)
const (
defaultDialTimeout = 5 * time.Second
)
type metadata struct {
dialTimeout time.Duration
}
func (d *tcpDialer) parseMetadata(md md.Metadata) (err error) {
return
}

View File

@ -1,68 +0,0 @@
package tls
import (
"context"
"crypto/tls"
"net"
"time"
"github.com/go-gost/gost/v3/pkg/dialer"
"github.com/go-gost/gost/v3/pkg/logger"
md "github.com/go-gost/gost/v3/pkg/metadata"
"github.com/go-gost/gost/v3/pkg/registry"
)
func init() {
registry.DialerRegistry().Register("tls", NewDialer)
}
type tlsDialer struct {
md metadata
logger logger.Logger
options dialer.Options
}
func NewDialer(opts ...dialer.Option) dialer.Dialer {
options := dialer.Options{}
for _, opt := range opts {
opt(&options)
}
return &tlsDialer{
logger: options.Logger,
options: options,
}
}
func (d *tlsDialer) Init(md md.Metadata) (err error) {
return d.parseMetadata(md)
}
func (d *tlsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOption) (net.Conn, error) {
var options dialer.DialOptions
for _, opt := range opts {
opt(&options)
}
conn, err := options.NetDialer.Dial(ctx, "tcp", addr)
if err != nil {
d.logger.Error(err)
}
return conn, err
}
// Handshake implements dialer.Handshaker
func (d *tlsDialer) Handshake(ctx context.Context, conn net.Conn, options ...dialer.HandshakeOption) (net.Conn, error) {
if d.md.handshakeTimeout > 0 {
conn.SetDeadline(time.Now().Add(d.md.handshakeTimeout))
defer conn.SetDeadline(time.Time{})
}
tlsConn := tls.Client(conn, d.options.TLSConfig)
if err := tlsConn.HandshakeContext(ctx); err != nil {
conn.Close()
return nil, err
}
return tlsConn, nil
}

View File

@ -1,21 +0,0 @@
package tls
import (
"time"
mdata "github.com/go-gost/gost/v3/pkg/metadata"
)
type metadata struct {
handshakeTimeout time.Duration
}
func (d *tlsDialer) parseMetadata(md mdata.Metadata) (err error) {
const (
handshakeTimeout = "handshakeTimeout"
)
d.md.handshakeTimeout = mdata.GetDuration(md, handshakeTimeout)
return
}

View File

@ -1,17 +0,0 @@
package udp
import "net"
type conn struct {
*net.UDPConn
}
func (c *conn) WriteTo(b []byte, addr net.Addr) (int, error) {
return c.UDPConn.Write(b)
}
func (c *conn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
n, err = c.UDPConn.Read(b)
addr = c.RemoteAddr()
return
}

View File

@ -1,50 +0,0 @@
package udp
import (
"context"
"net"
"github.com/go-gost/gost/v3/pkg/dialer"
"github.com/go-gost/gost/v3/pkg/logger"
md "github.com/go-gost/gost/v3/pkg/metadata"
"github.com/go-gost/gost/v3/pkg/registry"
)
func init() {
registry.DialerRegistry().Register("udp", NewDialer)
}
type udpDialer struct {
md metadata
logger logger.Logger
}
func NewDialer(opts ...dialer.Option) dialer.Dialer {
options := &dialer.Options{}
for _, opt := range opts {
opt(options)
}
return &udpDialer{
logger: options.Logger,
}
}
func (d *udpDialer) Init(md md.Metadata) (err error) {
return d.parseMetadata(md)
}
func (d *udpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOption) (net.Conn, error) {
var options dialer.DialOptions
for _, opt := range opts {
opt(&options)
}
c, err := options.NetDialer.Dial(ctx, "udp", addr)
if err != nil {
return nil, err
}
return &conn{
UDPConn: c.(*net.UDPConn),
}, nil
}

View File

@ -1,23 +0,0 @@
package udp
import (
"time"
md "github.com/go-gost/gost/v3/pkg/metadata"
)
const (
dialTimeout = "dialTimeout"
)
const (
defaultDialTimeout = 5 * time.Second
)
type metadata struct {
dialTimeout time.Duration
}
func (d *udpDialer) parseMetadata(md md.Metadata) (err error) {
return
}

View File

@ -1,115 +0,0 @@
package auto
import (
"bufio"
"context"
"net"
"time"
"github.com/go-gost/gosocks4"
"github.com/go-gost/gosocks5"
netpkg "github.com/go-gost/gost/v3/pkg/common/net"
"github.com/go-gost/gost/v3/pkg/handler"
md "github.com/go-gost/gost/v3/pkg/metadata"
"github.com/go-gost/gost/v3/pkg/registry"
)
func init() {
registry.HandlerRegistry().Register("auto", NewHandler)
}
type autoHandler struct {
httpHandler handler.Handler
socks4Handler handler.Handler
socks5Handler handler.Handler
options handler.Options
}
func NewHandler(opts ...handler.Option) handler.Handler {
options := handler.Options{}
for _, opt := range opts {
opt(&options)
}
h := &autoHandler{
options: options,
}
if f := registry.HandlerRegistry().Get("http"); f != nil {
v := append(opts,
handler.LoggerOption(options.Logger.WithFields(map[string]any{"type": "http"})))
h.httpHandler = f(v...)
}
if f := registry.HandlerRegistry().Get("socks4"); f != nil {
v := append(opts,
handler.LoggerOption(options.Logger.WithFields(map[string]any{"type": "socks4"})))
h.socks4Handler = f(v...)
}
if f := registry.HandlerRegistry().Get("socks5"); f != nil {
v := append(opts,
handler.LoggerOption(options.Logger.WithFields(map[string]any{"type": "socks5"})))
h.socks5Handler = f(v...)
}
return h
}
func (h *autoHandler) Init(md md.Metadata) error {
if h.httpHandler != nil {
if err := h.httpHandler.Init(md); err != nil {
return err
}
}
if h.socks4Handler != nil {
if err := h.socks4Handler.Init(md); err != nil {
return err
}
}
if h.socks5Handler != nil {
if err := h.socks5Handler.Init(md); err != nil {
return err
}
}
return nil
}
func (h *autoHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
log := h.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
})
start := time.Now()
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
defer func() {
log.WithFields(map[string]any{
"duration": time.Since(start),
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
}()
br := bufio.NewReader(conn)
b, err := br.Peek(1)
if err != nil {
log.Error(err)
conn.Close()
return err
}
conn = netpkg.NewBufferReaderConn(conn, br)
switch b[0] {
case gosocks4.Ver4: // socks4
if h.socks4Handler != nil {
return h.socks4Handler.Handle(ctx, conn)
}
case gosocks5.Ver5: // socks5
if h.socks5Handler != nil {
return h.socks5Handler.Handle(ctx, conn)
}
default: // http
if h.httpHandler != nil {
return h.httpHandler.Handle(ctx, conn)
}
}
return nil
}

View File

@ -1,117 +0,0 @@
package local
import (
"context"
"errors"
"fmt"
"net"
"time"
"github.com/go-gost/gost/v3/pkg/chain"
netpkg "github.com/go-gost/gost/v3/pkg/common/net"
"github.com/go-gost/gost/v3/pkg/handler"
md "github.com/go-gost/gost/v3/pkg/metadata"
"github.com/go-gost/gost/v3/pkg/registry"
)
func init() {
registry.HandlerRegistry().Register("tcp", NewHandler)
registry.HandlerRegistry().Register("udp", NewHandler)
registry.HandlerRegistry().Register("forward", NewHandler)
}
type forwardHandler struct {
group *chain.NodeGroup
router *chain.Router
md metadata
options handler.Options
}
func NewHandler(opts ...handler.Option) handler.Handler {
options := handler.Options{}
for _, opt := range opts {
opt(&options)
}
return &forwardHandler{
options: options,
}
}
func (h *forwardHandler) Init(md md.Metadata) (err error) {
if err = h.parseMetadata(md); err != nil {
return
}
if h.group == nil {
// dummy node used by relay connector.
h.group = chain.NewNodeGroup(&chain.Node{Name: "dummy", Addr: ":0"})
}
h.router = h.options.Router
if h.router == nil {
h.router = (&chain.Router{}).WithLogger(h.options.Logger)
}
return
}
// Forward implements handler.Forwarder.
func (h *forwardHandler) Forward(group *chain.NodeGroup) {
h.group = group
}
func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
defer conn.Close()
start := time.Now()
log := h.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
})
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
defer func() {
log.WithFields(map[string]any{
"duration": time.Since(start),
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
}()
target := h.group.Next()
if target == nil {
err := errors.New("target not available")
log.Error(err)
return err
}
network := "tcp"
if _, ok := conn.(net.PacketConn); ok {
network = "udp"
}
log = log.WithFields(map[string]any{
"dst": fmt.Sprintf("%s/%s", target.Addr, network),
})
log.Infof("%s >> %s", conn.RemoteAddr(), target.Addr)
cc, err := h.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,
// the dead marker may be a wrong operation.
target.Marker.Mark()
return err
}
defer cc.Close()
target.Marker.Reset()
t := time.Now()
log.Infof("%s <-> %s", conn.RemoteAddr(), target.Addr)
netpkg.Transport(conn, cc)
log.WithFields(map[string]any{
"duration": time.Since(t),
}).Infof("%s >-< %s", conn.RemoteAddr(), target.Addr)
return nil
}

View File

@ -1,20 +0,0 @@
package local
import (
"time"
mdata "github.com/go-gost/gost/v3/pkg/metadata"
)
type metadata struct {
readTimeout time.Duration
}
func (h *forwardHandler) parseMetadata(md mdata.Metadata) (err error) {
const (
readTimeout = "readTimeout"
)
h.md.readTimeout = mdata.GetDuration(md, readTimeout)
return
}

View File

@ -1,111 +0,0 @@
package remote
import (
"context"
"errors"
"fmt"
"net"
"time"
"github.com/go-gost/gost/v3/pkg/chain"
netpkg "github.com/go-gost/gost/v3/pkg/common/net"
"github.com/go-gost/gost/v3/pkg/handler"
md "github.com/go-gost/gost/v3/pkg/metadata"
"github.com/go-gost/gost/v3/pkg/registry"
)
func init() {
registry.HandlerRegistry().Register("rtcp", NewHandler)
registry.HandlerRegistry().Register("rudp", NewHandler)
}
type forwardHandler struct {
group *chain.NodeGroup
router *chain.Router
md metadata
options handler.Options
}
func NewHandler(opts ...handler.Option) handler.Handler {
options := handler.Options{}
for _, opt := range opts {
opt(&options)
}
return &forwardHandler{
options: options,
}
}
func (h *forwardHandler) Init(md md.Metadata) (err error) {
if err = h.parseMetadata(md); err != nil {
return
}
h.router = h.options.Router
if h.router == nil {
h.router = (&chain.Router{}).WithLogger(h.options.Logger)
}
return
}
// Forward implements handler.Forwarder.
func (h *forwardHandler) Forward(group *chain.NodeGroup) {
h.group = group
}
func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
defer conn.Close()
start := time.Now()
log := h.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
})
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
defer func() {
log.WithFields(map[string]any{
"duration": time.Since(start),
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
}()
target := h.group.Next()
if target == nil {
err := errors.New("target not available")
log.Error(err)
return err
}
network := "tcp"
if _, ok := conn.(net.PacketConn); ok {
network = "udp"
}
log = log.WithFields(map[string]any{
"dst": fmt.Sprintf("%s/%s", target.Addr, network),
})
log.Infof("%s >> %s", conn.RemoteAddr(), target.Addr)
cc, err := h.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,
// the dead marker may be a wrong operation.
target.Marker.Mark()
return err
}
defer cc.Close()
target.Marker.Reset()
t := time.Now()
log.Infof("%s <-> %s", conn.RemoteAddr(), target.Addr)
netpkg.Transport(conn, cc)
log.WithFields(map[string]any{
"duration": time.Since(t),
}).Infof("%s >-< %s", conn.RemoteAddr(), target.Addr)
return nil
}

View File

@ -1,20 +0,0 @@
package remote
import (
"time"
mdata "github.com/go-gost/gost/v3/pkg/metadata"
)
type metadata struct {
readTimeout time.Duration
}
func (h *forwardHandler) parseMetadata(md mdata.Metadata) (err error) {
const (
readTimeout = "readTimeout"
)
h.md.readTimeout = mdata.GetDuration(md, readTimeout)
return
}

View File

@ -1,18 +0,0 @@
package handler
import (
"context"
"net"
"github.com/go-gost/gost/v3/pkg/chain"
"github.com/go-gost/gost/v3/pkg/metadata"
)
type Handler interface {
Init(metadata.Metadata) error
Handle(context.Context, net.Conn, ...HandleOption) error
}
type Forwarder interface {
Forward(*chain.NodeGroup)
}

View File

@ -1,337 +0,0 @@
package http
import (
"bufio"
"context"
"encoding/base64"
"encoding/binary"
"errors"
"hash/crc32"
"net"
"net/http"
"net/http/httputil"
"os"
"strconv"
"strings"
"time"
"github.com/asaskevich/govalidator"
"github.com/go-gost/gost/v3/pkg/chain"
netpkg "github.com/go-gost/gost/v3/pkg/common/net"
"github.com/go-gost/gost/v3/pkg/handler"
"github.com/go-gost/gost/v3/pkg/logger"
md "github.com/go-gost/gost/v3/pkg/metadata"
"github.com/go-gost/gost/v3/pkg/registry"
)
func init() {
registry.HandlerRegistry().Register("http", NewHandler)
}
type httpHandler struct {
router *chain.Router
md metadata
options handler.Options
}
func NewHandler(opts ...handler.Option) handler.Handler {
options := handler.Options{}
for _, opt := range opts {
opt(&options)
}
return &httpHandler{
options: options,
}
}
func (h *httpHandler) Init(md md.Metadata) error {
if err := h.parseMetadata(md); err != nil {
return err
}
h.router = h.options.Router
if h.router == nil {
h.router = (&chain.Router{}).WithLogger(h.options.Logger)
}
return nil
}
func (h *httpHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
defer conn.Close()
start := time.Now()
log := h.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
})
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
defer func() {
log.WithFields(map[string]any{
"duration": time.Since(start),
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
}()
req, err := http.ReadRequest(bufio.NewReader(conn))
if err != nil {
log.Error(err)
return err
}
defer req.Body.Close()
return h.handleRequest(ctx, conn, req, log)
}
func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *http.Request, log logger.Logger) error {
if h.md.sni && !req.URL.IsAbs() && govalidator.IsDNSName(req.Host) {
req.URL.Scheme = "http"
}
network := req.Header.Get("X-Gost-Protocol")
if network != "udp" {
network = "tcp"
}
// Try to get the actual host.
// Compatible with GOST 2.x.
if v := req.Header.Get("Gost-Target"); v != "" {
if h, err := h.decodeServerName(v); err == nil {
req.Host = h
}
}
req.Header.Del("Gost-Target")
if v := req.Header.Get("X-Gost-Target"); v != "" {
if h, err := h.decodeServerName(v); err == nil {
req.Host = h
}
}
req.Header.Del("X-Gost-Target")
addr := req.Host
if _, port, _ := net.SplitHostPort(addr); port == "" {
addr = net.JoinHostPort(addr, "80")
}
fields := map[string]any{
"dst": addr,
}
if u, _, _ := h.basicProxyAuth(req.Header.Get("Proxy-Authorization"), log); u != "" {
fields["user"] = u
}
log = log.WithFields(fields)
if log.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpRequest(req, false)
log.Debug(string(dump))
}
log.Infof("%s >> %s", conn.RemoteAddr(), addr)
resp := &http.Response{
ProtoMajor: 1,
ProtoMinor: 1,
Header: h.md.header,
}
if resp.Header == nil {
resp.Header = http.Header{}
}
if h.options.Bypass != nil && h.options.Bypass.Contains(addr) {
resp.StatusCode = http.StatusForbidden
if log.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpResponse(resp, false)
log.Debug(string(dump))
}
log.Info("bypass: ", addr)
return resp.Write(conn)
}
if !h.authenticate(conn, req, resp, log) {
return nil
}
if network == "udp" {
return h.handleUDP(ctx, conn, network, req.Host, log)
}
if req.Method == "PRI" ||
(req.Method != http.MethodConnect && req.URL.Scheme != "http") {
resp.StatusCode = http.StatusBadRequest
if log.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpResponse(resp, false)
log.Debug(string(dump))
}
return resp.Write(conn)
}
req.Header.Del("Proxy-Authorization")
cc, err := h.router.Dial(ctx, network, addr)
if err != nil {
resp.StatusCode = http.StatusServiceUnavailable
if log.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpResponse(resp, false)
log.Debug(string(dump))
}
resp.Write(conn)
return err
}
defer cc.Close()
if req.Method == http.MethodConnect {
resp.StatusCode = http.StatusOK
resp.Status = "200 Connection established"
if log.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpResponse(resp, false)
log.Debug(string(dump))
}
if err = resp.Write(conn); err != nil {
log.Error(err)
return err
}
} else {
req.Header.Del("Proxy-Connection")
if err = req.Write(cc); err != nil {
log.Error(err)
return err
}
}
start := time.Now()
log.Infof("%s <-> %s", conn.RemoteAddr(), addr)
netpkg.Transport(conn, cc)
log.WithFields(map[string]any{
"duration": time.Since(start),
}).Infof("%s >-< %s", conn.RemoteAddr(), addr)
return nil
}
func (h *httpHandler) decodeServerName(s string) (string, error) {
b, err := base64.RawURLEncoding.DecodeString(s)
if err != nil {
return "", err
}
if len(b) < 4 {
return "", errors.New("invalid name")
}
v, err := base64.RawURLEncoding.DecodeString(string(b[4:]))
if err != nil {
return "", err
}
if crc32.ChecksumIEEE(v) != binary.BigEndian.Uint32(b[:4]) {
return "", errors.New("invalid name")
}
return string(v), nil
}
func (h *httpHandler) basicProxyAuth(proxyAuth string, log logger.Logger) (username, password string, ok bool) {
if proxyAuth == "" {
return
}
if !strings.HasPrefix(proxyAuth, "Basic ") {
return
}
c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(proxyAuth, "Basic "))
if err != nil {
return
}
cs := string(c)
s := strings.IndexByte(cs, ':')
if s < 0 {
return
}
return cs[:s], cs[s+1:], true
}
func (h *httpHandler) authenticate(conn net.Conn, req *http.Request, resp *http.Response, log logger.Logger) (ok bool) {
u, p, _ := h.basicProxyAuth(req.Header.Get("Proxy-Authorization"), log)
if h.options.Auther == nil || h.options.Auther.Authenticate(u, p) {
return true
}
pr := h.md.probeResistance
// probing resistance is enabled, and knocking host is mismatch.
if pr != nil && (pr.Knock == "" || !strings.EqualFold(req.URL.Hostname(), pr.Knock)) {
resp.StatusCode = http.StatusServiceUnavailable // default status code
switch pr.Type {
case "code":
resp.StatusCode, _ = strconv.Atoi(pr.Value)
case "web":
url := pr.Value
if !strings.HasPrefix(url, "http") {
url = "http://" + url
}
r, err := http.Get(url)
if err != nil {
log.Error(err)
break
}
resp = r
defer resp.Body.Close()
case "host":
cc, err := net.Dial("tcp", pr.Value)
if err != nil {
log.Error(err)
break
}
defer cc.Close()
req.Write(cc)
netpkg.Transport(conn, cc)
return
case "file":
f, _ := os.Open(pr.Value)
if f != nil {
defer f.Close()
resp.StatusCode = http.StatusOK
if finfo, _ := f.Stat(); finfo != nil {
resp.ContentLength = finfo.Size()
}
resp.Header.Set("Content-Type", "text/html")
resp.Body = f
}
}
}
if resp.Header == nil {
resp.Header = http.Header{}
}
if resp.StatusCode == 0 {
resp.StatusCode = http.StatusProxyAuthRequired
resp.Header.Add("Proxy-Authenticate", "Basic realm=\"gost\"")
if strings.ToLower(req.Header.Get("Proxy-Connection")) == "keep-alive" {
// XXX libcurl will keep sending auth request in same conn
// which we don't supported yet.
resp.Header.Add("Connection", "close")
resp.Header.Add("Proxy-Connection", "close")
}
log.Info("proxy authentication required")
} else {
resp.Header.Set("Server", "nginx/1.20.1")
resp.Header.Set("Date", time.Now().Format(http.TimeFormat))
if resp.StatusCode == http.StatusOK {
resp.Header.Set("Connection", "keep-alive")
}
}
if log.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpResponse(resp, false)
log.Debug(string(dump))
}
resp.Write(conn)
return
}

View File

@ -1,53 +0,0 @@
package http
import (
"net/http"
"strings"
mdata "github.com/go-gost/gost/v3/pkg/metadata"
)
type metadata struct {
probeResistance *probeResistance
sni bool
enableUDP bool
header http.Header
}
func (h *httpHandler) parseMetadata(md mdata.Metadata) error {
const (
header = "header"
probeResistKey = "probeResistance"
knock = "knock"
sni = "sni"
enableUDP = "udp"
)
if m := mdata.GetStringMapString(md, header); len(m) > 0 {
hd := http.Header{}
for k, v := range m {
hd.Add(k, v)
}
h.md.header = hd
}
if v := mdata.GetString(md, probeResistKey); v != "" {
if ss := strings.SplitN(v, ":", 2); len(ss) == 2 {
h.md.probeResistance = &probeResistance{
Type: ss[0],
Value: ss[1],
Knock: mdata.GetString(md, knock),
}
}
}
h.md.sni = mdata.GetBool(md, sni)
h.md.enableUDP = mdata.GetBool(md, enableUDP)
return nil
}
type probeResistance struct {
Type string
Value string
Knock string
}

View File

@ -1,80 +0,0 @@
package http
import (
"context"
"errors"
"net"
"net/http"
"net/http/httputil"
"time"
"github.com/go-gost/gost/v3/pkg/common/net/relay"
"github.com/go-gost/gost/v3/pkg/internal/util/socks"
"github.com/go-gost/gost/v3/pkg/logger"
)
func (h *httpHandler) handleUDP(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) error {
log = log.WithFields(map[string]any{
"cmd": "udp",
})
resp := &http.Response{
ProtoMajor: 1,
ProtoMinor: 1,
Header: h.md.header,
}
if resp.Header == nil {
resp.Header = http.Header{}
}
if !h.md.enableUDP {
resp.StatusCode = http.StatusForbidden
if log.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpResponse(resp, false)
log.Debug(string(dump))
}
log.Error("http: UDP relay is disabled")
return resp.Write(conn)
}
resp.StatusCode = http.StatusOK
if log.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpResponse(resp, false)
log.Debug(string(dump))
}
if err := resp.Write(conn); err != nil {
log.Error(err)
return err
}
// obtain a udp connection
c, err := h.router.Dial(ctx, "udp", "") // UDP association
if err != nil {
log.Error(err)
return err
}
defer c.Close()
pc, ok := c.(net.PacketConn)
if !ok {
err = errors.New("wrong connection type")
log.Error(err)
return err
}
relay := relay.NewUDPRelay(socks.UDPTunServerConn(conn), pc).
WithBypass(h.options.Bypass).
WithLogger(log)
t := time.Now()
log.Infof("%s <-> %s", conn.RemoteAddr(), pc.LocalAddr())
relay.Run()
log.WithFields(map[string]any{
"duration": time.Since(t),
}).Infof("%s >-< %s", conn.RemoteAddr(), pc.LocalAddr())
return nil
}

View File

@ -1,71 +0,0 @@
package handler
import (
"crypto/tls"
"net/url"
"github.com/go-gost/gost/v3/pkg/auth"
"github.com/go-gost/gost/v3/pkg/bypass"
"github.com/go-gost/gost/v3/pkg/chain"
"github.com/go-gost/gost/v3/pkg/logger"
"github.com/go-gost/gost/v3/pkg/metadata"
)
type Options struct {
Bypass bypass.Bypass
Router *chain.Router
Auth *url.Userinfo
Auther auth.Authenticator
TLSConfig *tls.Config
Logger logger.Logger
}
type Option func(opts *Options)
func BypassOption(bypass bypass.Bypass) Option {
return func(opts *Options) {
opts.Bypass = bypass
}
}
func RouterOption(router *chain.Router) Option {
return func(opts *Options) {
opts.Router = router
}
}
func AuthOption(auth *url.Userinfo) Option {
return func(opts *Options) {
opts.Auth = auth
}
}
func AutherOption(auther auth.Authenticator) Option {
return func(opts *Options) {
opts.Auther = auther
}
}
func TLSConfigOption(tlsConfig *tls.Config) Option {
return func(opts *Options) {
opts.TLSConfig = tlsConfig
}
}
func LoggerOption(logger logger.Logger) Option {
return func(opts *Options) {
opts.Logger = logger
}
}
type HandleOptions struct {
Metadata metadata.Metadata
}
type HandleOption func(opts *HandleOptions)
func MetadataHandleOption(md metadata.Metadata) HandleOption {
return func(opts *HandleOptions) {
opts.Metadata = md
}
}

View File

@ -1,152 +0,0 @@
package v4
import (
"context"
"errors"
"net"
"time"
"github.com/go-gost/gosocks4"
"github.com/go-gost/gost/v3/pkg/chain"
netpkg "github.com/go-gost/gost/v3/pkg/common/net"
"github.com/go-gost/gost/v3/pkg/handler"
"github.com/go-gost/gost/v3/pkg/logger"
md "github.com/go-gost/gost/v3/pkg/metadata"
"github.com/go-gost/gost/v3/pkg/registry"
)
var (
ErrUnknownCmd = errors.New("socks4: unknown command")
ErrUnimplemented = errors.New("socks4: unimplemented")
)
func init() {
registry.HandlerRegistry().Register("socks4", NewHandler)
registry.HandlerRegistry().Register("socks4a", NewHandler)
}
type socks4Handler struct {
router *chain.Router
md metadata
options handler.Options
}
func NewHandler(opts ...handler.Option) handler.Handler {
options := handler.Options{}
for _, opt := range opts {
opt(&options)
}
return &socks4Handler{
options: options,
}
}
func (h *socks4Handler) Init(md md.Metadata) (err error) {
if err := h.parseMetadata(md); err != nil {
return err
}
h.router = h.options.Router
if h.router == nil {
h.router = (&chain.Router{}).WithLogger(h.options.Logger)
}
return nil
}
func (h *socks4Handler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
defer conn.Close()
start := time.Now()
log := h.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
})
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
defer func() {
log.WithFields(map[string]any{
"duration": time.Since(start),
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
}()
if h.md.readTimeout > 0 {
conn.SetReadDeadline(time.Now().Add(h.md.readTimeout))
}
req, err := gosocks4.ReadRequest(conn)
if err != nil {
log.Error(err)
return err
}
log.Debug(req)
conn.SetReadDeadline(time.Time{})
if h.options.Auther != nil &&
!h.options.Auther.Authenticate(string(req.Userid), "") {
resp := gosocks4.NewReply(gosocks4.RejectedUserid, nil)
log.Debug(resp)
return resp.Write(conn)
}
switch req.Cmd {
case gosocks4.CmdConnect:
return h.handleConnect(ctx, conn, req, log)
case gosocks4.CmdBind:
return h.handleBind(ctx, conn, req)
default:
err = ErrUnknownCmd
log.Error(err)
return err
}
}
func (h *socks4Handler) handleConnect(ctx context.Context, conn net.Conn, req *gosocks4.Request, log logger.Logger) error {
addr := req.Addr.String()
log = log.WithFields(map[string]any{
"dst": addr,
})
log.Infof("%s >> %s", conn.RemoteAddr(), addr)
if h.options.Bypass != nil && h.options.Bypass.Contains(addr) {
resp := gosocks4.NewReply(gosocks4.Rejected, nil)
log.Debug(resp)
log.Info("bypass: ", addr)
return resp.Write(conn)
}
cc, err := h.router.Dial(ctx, "tcp", addr)
if err != nil {
resp := gosocks4.NewReply(gosocks4.Failed, nil)
resp.Write(conn)
log.Debug(resp)
return err
}
defer cc.Close()
resp := gosocks4.NewReply(gosocks4.Granted, nil)
if err := resp.Write(conn); err != nil {
log.Error(err)
return err
}
log.Debug(resp)
t := time.Now()
log.Infof("%s <-> %s", conn.RemoteAddr(), addr)
netpkg.Transport(conn, cc)
log.WithFields(map[string]any{
"duration": time.Since(t),
}).Infof("%s >-< %s", conn.RemoteAddr(), addr)
return nil
}
func (h *socks4Handler) handleBind(ctx context.Context, conn net.Conn, req *gosocks4.Request) error {
// TODO: bind
return ErrUnimplemented
}

View File

@ -1,20 +0,0 @@
package v4
import (
"time"
mdata "github.com/go-gost/gost/v3/pkg/metadata"
)
type metadata struct {
readTimeout time.Duration
}
func (h *socks4Handler) parseMetadata(md mdata.Metadata) (err error) {
const (
readTimeout = "readTimeout"
)
h.md.readTimeout = mdata.GetDuration(md, readTimeout)
return
}

View File

@ -1,149 +0,0 @@
package v5
import (
"context"
"fmt"
"net"
"time"
"github.com/go-gost/gosocks5"
netpkg "github.com/go-gost/gost/v3/pkg/common/net"
"github.com/go-gost/gost/v3/pkg/logger"
)
func (h *socks5Handler) handleBind(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) error {
log = log.WithFields(map[string]any{
"dst": fmt.Sprintf("%s/%s", address, network),
"cmd": "bind",
})
log.Infof("%s >> %s", conn.RemoteAddr(), address)
if !h.md.enableBind {
reply := gosocks5.NewReply(gosocks5.NotAllowed, nil)
log.Debug(reply)
log.Error("socks5: BIND is disabled")
return reply.Write(conn)
}
// BIND does not support chain.
return h.bindLocal(ctx, conn, network, address, log)
}
func (h *socks5Handler) bindLocal(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) error {
ln, err := net.Listen(network, address) // strict mode: if the port already in use, it will return error
if err != nil {
log.Error(err)
reply := gosocks5.NewReply(gosocks5.Failure, nil)
if err := reply.Write(conn); err != nil {
log.Error(err)
}
log.Debug(reply)
return err
}
socksAddr := gosocks5.Addr{}
if err := socksAddr.ParseFrom(ln.Addr().String()); err != nil {
log.Warn(err)
}
// Issue: may not reachable when host has multi-interface
socksAddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String())
socksAddr.Type = 0
reply := gosocks5.NewReply(gosocks5.Succeeded, &socksAddr)
if err := reply.Write(conn); err != nil {
log.Error(err)
ln.Close()
return err
}
log.Debug(reply)
log = log.WithFields(map[string]any{
"bind": fmt.Sprintf("%s/%s", ln.Addr(), ln.Addr().Network()),
})
log.Debugf("bind on %s OK", ln.Addr())
h.serveBind(ctx, conn, ln, log)
return nil
}
func (h *socks5Handler) serveBind(ctx context.Context, conn net.Conn, ln net.Listener, log logger.Logger) {
var rc net.Conn
accept := func() <-chan error {
errc := make(chan error, 1)
go func() {
defer close(errc)
defer ln.Close()
c, err := ln.Accept()
if err != nil {
errc <- err
}
rc = c
}()
return errc
}
pc1, pc2 := net.Pipe()
pipe := func() <-chan error {
errc := make(chan error, 1)
go func() {
defer close(errc)
defer pc1.Close()
errc <- netpkg.Transport(conn, pc1)
}()
return errc
}
defer pc2.Close()
select {
case err := <-accept():
if err != nil {
log.Error(err)
reply := gosocks5.NewReply(gosocks5.Failure, nil)
if err := reply.Write(pc2); err != nil {
log.Error(err)
}
log.Debug(reply)
return
}
defer rc.Close()
log.Debugf("peer %s accepted", rc.RemoteAddr())
log = log.WithFields(map[string]any{
"local": rc.LocalAddr().String(),
"remote": rc.RemoteAddr().String(),
})
raddr := gosocks5.Addr{}
raddr.ParseFrom(rc.RemoteAddr().String())
reply := gosocks5.NewReply(gosocks5.Succeeded, &raddr)
if err := reply.Write(pc2); err != nil {
log.Error(err)
}
log.Debug(reply)
start := time.Now()
log.Infof("%s <-> %s", rc.LocalAddr(), rc.RemoteAddr())
netpkg.Transport(pc2, rc)
log.WithFields(map[string]any{"duration": time.Since(start)}).
Infof("%s >-< %s", rc.LocalAddr(), rc.RemoteAddr())
case err := <-pipe():
if err != nil {
log.Error(err)
}
ln.Close()
return
}
}

View File

@ -1,53 +0,0 @@
package v5
import (
"context"
"fmt"
"net"
"time"
"github.com/go-gost/gosocks5"
netpkg "github.com/go-gost/gost/v3/pkg/common/net"
"github.com/go-gost/gost/v3/pkg/logger"
)
func (h *socks5Handler) handleConnect(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) error {
log = log.WithFields(map[string]any{
"dst": fmt.Sprintf("%s/%s", address, network),
"cmd": "connect",
})
log.Infof("%s >> %s", conn.RemoteAddr(), address)
if h.options.Bypass != nil && h.options.Bypass.Contains(address) {
resp := gosocks5.NewReply(gosocks5.NotAllowed, nil)
log.Debug(resp)
log.Info("bypass: ", address)
return resp.Write(conn)
}
cc, err := h.router.Dial(ctx, network, address)
if err != nil {
resp := gosocks5.NewReply(gosocks5.NetUnreachable, nil)
log.Debug(resp)
resp.Write(conn)
return err
}
defer cc.Close()
resp := gosocks5.NewReply(gosocks5.Succeeded, nil)
if err := resp.Write(conn); err != nil {
log.Error(err)
return err
}
log.Debug(resp)
t := time.Now()
log.Infof("%s <-> %s", conn.RemoteAddr(), address)
netpkg.Transport(conn, cc)
log.WithFields(map[string]any{
"duration": time.Since(t),
}).Infof("%s >-< %s", conn.RemoteAddr(), address)
return nil
}

View File

@ -1,115 +0,0 @@
package v5
import (
"context"
"errors"
"net"
"time"
"github.com/go-gost/gosocks5"
"github.com/go-gost/gost/v3/pkg/chain"
"github.com/go-gost/gost/v3/pkg/handler"
"github.com/go-gost/gost/v3/pkg/internal/util/socks"
md "github.com/go-gost/gost/v3/pkg/metadata"
"github.com/go-gost/gost/v3/pkg/registry"
)
var (
ErrUnknownCmd = errors.New("socks5: unknown command")
)
func init() {
registry.HandlerRegistry().Register("socks5", NewHandler)
registry.HandlerRegistry().Register("socks", NewHandler)
}
type socks5Handler struct {
selector gosocks5.Selector
router *chain.Router
md metadata
options handler.Options
}
func NewHandler(opts ...handler.Option) handler.Handler {
options := handler.Options{}
for _, opt := range opts {
opt(&options)
}
return &socks5Handler{
options: options,
}
}
func (h *socks5Handler) Init(md md.Metadata) (err error) {
if err = h.parseMetadata(md); err != nil {
return
}
h.router = h.options.Router
if h.router == nil {
h.router = (&chain.Router{}).WithLogger(h.options.Logger)
}
h.selector = &serverSelector{
Authenticator: h.options.Auther,
TLSConfig: h.options.TLSConfig,
logger: h.options.Logger,
noTLS: h.md.noTLS,
}
return
}
func (h *socks5Handler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
defer conn.Close()
start := time.Now()
log := h.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
})
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
defer func() {
log.WithFields(map[string]any{
"duration": time.Since(start),
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
}()
if h.md.readTimeout > 0 {
conn.SetReadDeadline(time.Now().Add(h.md.readTimeout))
}
conn = gosocks5.ServerConn(conn, h.selector)
req, err := gosocks5.ReadRequest(conn)
if err != nil {
log.Error(err)
return err
}
log.Debug(req)
conn.SetReadDeadline(time.Time{})
address := req.Addr.String()
switch req.Cmd {
case gosocks5.CmdConnect:
return h.handleConnect(ctx, conn, "tcp", address, log)
case gosocks5.CmdBind:
return h.handleBind(ctx, conn, "tcp", address, log)
case socks.CmdMuxBind:
return h.handleMuxBind(ctx, conn, "tcp", address, log)
case gosocks5.CmdUdp:
return h.handleUDP(ctx, conn, log)
case socks.CmdUDPTun:
return h.handleUDPTun(ctx, conn, "udp", address, log)
default:
err = ErrUnknownCmd
log.Error(err)
resp := gosocks5.NewReply(gosocks5.CmdUnsupported, nil)
resp.Write(conn)
log.Debug(resp)
return err
}
}

View File

@ -1,133 +0,0 @@
package v5
import (
"context"
"fmt"
"net"
"time"
"github.com/go-gost/gosocks5"
netpkg "github.com/go-gost/gost/v3/pkg/common/net"
"github.com/go-gost/gost/v3/pkg/internal/util/mux"
"github.com/go-gost/gost/v3/pkg/logger"
)
func (h *socks5Handler) handleMuxBind(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) error {
log = log.WithFields(map[string]any{
"dst": fmt.Sprintf("%s/%s", address, network),
"cmd": "mbind",
})
log.Infof("%s >> %s", conn.RemoteAddr(), address)
if !h.md.enableBind {
reply := gosocks5.NewReply(gosocks5.NotAllowed, nil)
log.Debug(reply)
log.Error("socks5: BIND is disabled")
return reply.Write(conn)
}
return h.muxBindLocal(ctx, conn, network, address, log)
}
func (h *socks5Handler) muxBindLocal(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) error {
ln, err := net.Listen(network, address) // strict mode: if the port already in use, it will return error
if err != nil {
log.Error(err)
reply := gosocks5.NewReply(gosocks5.Failure, nil)
if err := reply.Write(conn); err != nil {
log.Error(err)
}
log.Debug(reply)
return err
}
socksAddr := gosocks5.Addr{}
err = socksAddr.ParseFrom(ln.Addr().String())
if err != nil {
log.Warn(err)
}
// Issue: may not reachable when host has multi-interface
socksAddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String())
socksAddr.Type = 0
reply := gosocks5.NewReply(gosocks5.Succeeded, &socksAddr)
if err := reply.Write(conn); err != nil {
log.Error(err)
ln.Close()
return err
}
log.Debug(reply)
log = log.WithFields(map[string]any{
"bind": fmt.Sprintf("%s/%s", ln.Addr(), ln.Addr().Network()),
})
log.Debugf("bind on %s OK", ln.Addr())
return h.serveMuxBind(ctx, conn, ln, log)
}
func (h *socks5Handler) serveMuxBind(ctx context.Context, conn net.Conn, ln net.Listener, log logger.Logger) error {
// Upgrade connection to multiplex stream.
session, err := mux.ClientSession(conn)
if err != nil {
log.Error(err)
return err
}
defer session.Close()
go func() {
defer ln.Close()
for {
conn, err := session.Accept()
if err != nil {
log.Error(err)
return
}
conn.Close() // we do not handle incoming connections.
}
}()
for {
rc, err := ln.Accept()
if err != nil {
log.Error(err)
return err
}
log.Debugf("peer %s accepted", rc.RemoteAddr())
go func(c net.Conn) {
defer c.Close()
log = log.WithFields(map[string]any{
"local": rc.LocalAddr().String(),
"remote": rc.RemoteAddr().String(),
})
sc, err := session.GetConn()
if err != nil {
log.Error(err)
return
}
defer sc.Close()
// incompatible with GOST v2.x
if !h.md.compatibilityMode {
addr := gosocks5.Addr{}
addr.ParseFrom(c.RemoteAddr().String())
reply := gosocks5.NewReply(gosocks5.Succeeded, &addr)
if err := reply.Write(sc); err != nil {
log.Error(err)
return
}
log.Debug(reply)
}
t := time.Now()
log.Infof("%s <-> %s", c.LocalAddr(), c.RemoteAddr())
netpkg.Transport(sc, c)
log.WithFields(map[string]any{"duration": time.Since(t)}).
Infof("%s >-< %s", c.LocalAddr(), c.RemoteAddr())
}(rc)
}
}

View File

@ -1,43 +0,0 @@
package v5
import (
"math"
"time"
mdata "github.com/go-gost/gost/v3/pkg/metadata"
)
type metadata struct {
readTimeout time.Duration
noTLS bool
enableBind bool
enableUDP bool
udpBufferSize int
compatibilityMode bool
}
func (h *socks5Handler) parseMetadata(md mdata.Metadata) (err error) {
const (
readTimeout = "readTimeout"
noTLS = "notls"
enableBind = "bind"
enableUDP = "udp"
udpBufferSize = "udpBufferSize"
compatibilityMode = "comp"
)
h.md.readTimeout = mdata.GetDuration(md, readTimeout)
h.md.noTLS = mdata.GetBool(md, noTLS)
h.md.enableBind = mdata.GetBool(md, enableBind)
h.md.enableUDP = mdata.GetBool(md, enableUDP)
if bs := mdata.GetInt(md, udpBufferSize); bs > 0 {
h.md.udpBufferSize = int(math.Min(math.Max(float64(bs), 512), 64*1024))
} else {
h.md.udpBufferSize = 1024
}
h.md.compatibilityMode = mdata.GetBool(md, compatibilityMode)
return nil
}

View File

@ -1,90 +0,0 @@
package v5
import (
"crypto/tls"
"net"
"github.com/go-gost/gosocks5"
"github.com/go-gost/gost/v3/pkg/auth"
"github.com/go-gost/gost/v3/pkg/internal/util/socks"
"github.com/go-gost/gost/v3/pkg/logger"
)
type serverSelector struct {
methods []uint8
Authenticator auth.Authenticator
TLSConfig *tls.Config
logger logger.Logger
noTLS bool
}
func (selector *serverSelector) Methods() []uint8 {
return selector.methods
}
func (s *serverSelector) Select(methods ...uint8) (method uint8) {
s.logger.Debugf("%d %d %v", gosocks5.Ver5, len(methods), methods)
method = gosocks5.MethodNoAuth
for _, m := range methods {
if m == socks.MethodTLS && !s.noTLS {
method = m
break
}
}
// when Authenticator is set, auth is mandatory
if s.Authenticator != nil {
if method == gosocks5.MethodNoAuth {
method = gosocks5.MethodUserPass
}
if method == socks.MethodTLS && !s.noTLS {
method = socks.MethodTLSAuth
}
}
return
}
func (s *serverSelector) OnSelected(method uint8, conn net.Conn) (net.Conn, error) {
s.logger.Debugf("%d %d", gosocks5.Ver5, method)
switch method {
case socks.MethodTLS:
conn = tls.Server(conn, s.TLSConfig)
case gosocks5.MethodUserPass, socks.MethodTLSAuth:
if method == socks.MethodTLSAuth {
conn = tls.Server(conn, s.TLSConfig)
}
req, err := gosocks5.ReadUserPassRequest(conn)
if err != nil {
s.logger.Error(err)
return nil, err
}
s.logger.Debug(req)
if s.Authenticator != nil &&
!s.Authenticator.Authenticate(req.Username, req.Password) {
resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Failure)
if err := resp.Write(conn); err != nil {
s.logger.Error(err)
return nil, err
}
s.logger.Info(resp)
return nil, gosocks5.ErrAuthFailure
}
resp := gosocks5.NewUserPassResponse(gosocks5.UserPassVer, gosocks5.Succeeded)
if err := resp.Write(conn); err != nil {
s.logger.Error(err)
return nil, err
}
s.logger.Debug(resp)
case gosocks5.MethodNoAcceptable:
return nil, gosocks5.ErrBadMethod
}
return conn, nil
}

View File

@ -1,85 +0,0 @@
package v5
import (
"context"
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"time"
"github.com/go-gost/gosocks5"
"github.com/go-gost/gost/v3/pkg/common/net/relay"
"github.com/go-gost/gost/v3/pkg/internal/util/socks"
"github.com/go-gost/gost/v3/pkg/logger"
)
func (h *socks5Handler) handleUDP(ctx context.Context, conn net.Conn, log logger.Logger) error {
log = log.WithFields(map[string]any{
"cmd": "udp",
})
if !h.md.enableUDP {
reply := gosocks5.NewReply(gosocks5.NotAllowed, nil)
log.Debug(reply)
log.Error("socks5: UDP relay is disabled")
return reply.Write(conn)
}
cc, err := net.ListenUDP("udp", nil)
if err != nil {
log.Error(err)
reply := gosocks5.NewReply(gosocks5.Failure, nil)
reply.Write(conn)
log.Debug(reply)
return err
}
defer cc.Close()
saddr := gosocks5.Addr{}
saddr.ParseFrom(cc.LocalAddr().String())
saddr.Type = 0
saddr.Host, _, _ = net.SplitHostPort(conn.LocalAddr().String()) // replace the IP to the out-going interface's
reply := gosocks5.NewReply(gosocks5.Succeeded, &saddr)
if err := reply.Write(conn); err != nil {
log.Error(err)
return err
}
log.Debug(reply)
log = log.WithFields(map[string]any{
"bind": fmt.Sprintf("%s/%s", cc.LocalAddr(), cc.LocalAddr().Network()),
})
log.Debugf("bind on %s OK", cc.LocalAddr())
// obtain a udp connection
c, err := h.router.Dial(ctx, "udp", "") // UDP association
if err != nil {
log.Error(err)
return err
}
defer c.Close()
pc, ok := c.(net.PacketConn)
if !ok {
err := errors.New("socks5: wrong connection type")
log.Error(err)
return err
}
r := relay.NewUDPRelay(socks.UDPConn(cc, h.md.udpBufferSize), pc).
WithBypass(h.options.Bypass).
WithLogger(log)
r.SetBufferSize(h.md.udpBufferSize)
go r.Run()
t := time.Now()
log.Infof("%s <-> %s", conn.RemoteAddr(), cc.LocalAddr())
io.Copy(ioutil.Discard, conn)
log.WithFields(map[string]any{"duration": time.Since(t)}).
Infof("%s >-< %s", conn.RemoteAddr(), cc.LocalAddr())
return nil
}

View File

@ -1,72 +0,0 @@
package v5
import (
"context"
"net"
"time"
"github.com/go-gost/gosocks5"
"github.com/go-gost/gost/v3/pkg/common/net/relay"
"github.com/go-gost/gost/v3/pkg/internal/util/socks"
"github.com/go-gost/gost/v3/pkg/logger"
)
func (h *socks5Handler) handleUDPTun(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) error {
log = log.WithFields(map[string]any{
"cmd": "udp-tun",
})
bindAddr, _ := net.ResolveUDPAddr(network, address)
if bindAddr == nil {
bindAddr = &net.UDPAddr{}
}
if bindAddr.Port == 0 {
// relay mode
if !h.md.enableUDP {
reply := gosocks5.NewReply(gosocks5.NotAllowed, nil)
log.Debug(reply)
log.Error("socks5: UDP relay is disabled")
return reply.Write(conn)
}
} else {
// BIND mode
if !h.md.enableBind {
reply := gosocks5.NewReply(gosocks5.NotAllowed, nil)
log.Debug(reply)
log.Error("socks5: BIND is disabled")
return reply.Write(conn)
}
}
pc, err := net.ListenUDP(network, bindAddr)
if err != nil {
log.Error(err)
return err
}
defer pc.Close()
saddr := gosocks5.Addr{}
saddr.ParseFrom(pc.LocalAddr().String())
reply := gosocks5.NewReply(gosocks5.Succeeded, &saddr)
if err := reply.Write(conn); err != nil {
log.Error(err)
return err
}
log.Debug(reply)
log.Debugf("bind on %s OK", pc.LocalAddr())
r := relay.NewUDPRelay(socks.UDPTunServerConn(conn), pc).
WithBypass(h.options.Bypass).
WithLogger(log)
r.SetBufferSize(h.md.udpBufferSize)
t := time.Now()
log.Infof("%s <-> %s", conn.RemoteAddr(), pc.LocalAddr())
r.Run()
log.WithFields(map[string]any{
"duration": time.Since(t),
}).Infof("%s >-< %s", conn.RemoteAddr(), pc.LocalAddr())
return nil
}

View File

@ -1,132 +0,0 @@
package hosts
import (
"net"
"strings"
"sync"
"github.com/go-gost/gost/v3/pkg/logger"
)
// HostMapper is a mapping from hostname to IP.
type HostMapper interface {
Lookup(network, host string) ([]net.IP, bool)
}
type hostMapping struct {
IPs []net.IP
Hostname string
}
// Hosts is a static table lookup for hostnames.
// For each host a single line should be present with the following information:
// IP_address canonical_hostname [aliases...]
// Fields of the entry are separated by any number of blanks and/or tab characters.
// Text from a "#" character until the end of the line is a comment, and is ignored.
type Hosts struct {
mappings sync.Map
Logger logger.Logger
}
func NewHosts() *Hosts {
return &Hosts{}
}
// Map maps ip to hostname or aliases.
func (h *Hosts) Map(ip net.IP, hostname string, aliases ...string) {
if hostname == "" {
return
}
v, _ := h.mappings.Load(hostname)
m, _ := v.(*hostMapping)
if m == nil {
m = &hostMapping{
IPs: []net.IP{ip},
Hostname: hostname,
}
} else {
m.IPs = append(m.IPs, ip)
}
h.mappings.Store(hostname, m)
for _, alias := range aliases {
// indirect mapping from alias to hostname
if alias != "" {
h.mappings.Store(alias, &hostMapping{
Hostname: hostname,
})
}
}
}
// Lookup searches the IP address corresponds to the given network and host from the host table.
// The network should be 'ip', 'ip4' or 'ip6', default network is 'ip'.
// the host should be a hostname (example.org) or a hostname with dot prefix (.example.org).
func (h *Hosts) Lookup(network, host string) (ips []net.IP, ok bool) {
m := h.lookup(host)
if m == nil {
m = h.lookup("." + host)
}
if m == nil {
s := host
for {
if index := strings.IndexByte(s, '.'); index > 0 {
m = h.lookup(s[index:])
s = s[index+1:]
if m == nil {
continue
}
}
break
}
}
if m == nil {
return
}
// hostname alias
if !strings.HasPrefix(m.Hostname, ".") && host != m.Hostname {
m = h.lookup(m.Hostname)
if m == nil {
return
}
}
switch network {
case "ip4":
for _, ip := range m.IPs {
if ip = ip.To4(); ip != nil {
ips = append(ips, ip)
}
}
case "ip6":
for _, ip := range m.IPs {
if ip.To4() == nil {
ips = append(ips, ip)
}
}
default:
ips = m.IPs
}
if len(ips) > 0 {
h.Logger.Debugf("host mapper: %s -> %s", host, ips)
}
return
}
func (h *Hosts) lookup(host string) *hostMapping {
if h == nil || host == "" {
return nil
}
v, ok := h.mappings.Load(host)
if !ok {
return nil
}
m, _ := v.(*hostMapping)
return m
}

View File

@ -1,85 +0,0 @@
package mux
import (
"net"
smux "github.com/xtaci/smux"
)
type Session struct {
conn net.Conn
session *smux.Session
}
func ClientSession(conn net.Conn) (*Session, error) {
s, err := smux.Client(conn, smux.DefaultConfig())
if err != nil {
return nil, err
}
return &Session{
conn: conn,
session: s,
}, nil
}
func ServerSession(conn net.Conn) (*Session, error) {
s, err := smux.Server(conn, smux.DefaultConfig())
if err != nil {
return nil, err
}
return &Session{
conn: conn,
session: s,
}, nil
}
func (session *Session) GetConn() (net.Conn, error) {
stream, err := session.session.OpenStream()
if err != nil {
return nil, err
}
return &streamConn{Conn: session.conn, stream: stream}, nil
}
func (session *Session) Accept() (net.Conn, error) {
stream, err := session.session.AcceptStream()
if err != nil {
return nil, err
}
return &streamConn{Conn: session.conn, stream: stream}, nil
}
func (session *Session) Close() error {
if session.session == nil {
return nil
}
return session.session.Close()
}
func (session *Session) IsClosed() bool {
if session.session == nil {
return true
}
return session.session.IsClosed()
}
func (session *Session) NumStreams() int {
return session.session.NumStreams()
}
type streamConn struct {
net.Conn
stream *smux.Stream
}
func (c *streamConn) Read(b []byte) (n int, err error) {
return c.stream.Read(b)
}
func (c *streamConn) Write(b []byte) (n int, err error) {
return c.stream.Write(b)
}
func (c *streamConn) Close() error {
return c.stream.Close()
}

View File

@ -1,172 +0,0 @@
package socks
import (
"bytes"
"net"
"github.com/go-gost/gosocks5"
"github.com/go-gost/gost/v3/pkg/common/bufpool"
)
type udpTunConn struct {
net.Conn
taddr net.Addr
}
func UDPTunClientConn(c net.Conn, targetAddr net.Addr) net.Conn {
return &udpTunConn{
Conn: c,
taddr: targetAddr,
}
}
func UDPTunClientPacketConn(c net.Conn) net.PacketConn {
return &udpTunConn{
Conn: c,
}
}
func UDPTunServerConn(c net.Conn) net.PacketConn {
return &udpTunConn{
Conn: c,
}
}
func (c *udpTunConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
socksAddr := gosocks5.Addr{}
header := gosocks5.UDPHeader{
Addr: &socksAddr,
}
dgram := gosocks5.UDPDatagram{
Header: &header,
Data: b,
}
_, err = dgram.ReadFrom(c.Conn)
if err != nil {
return
}
n = len(dgram.Data)
if n > len(b) {
n = copy(b, dgram.Data)
}
addr, err = net.ResolveUDPAddr("udp", socksAddr.String())
return
}
func (c *udpTunConn) Read(b []byte) (n int, err error) {
n, _, err = c.ReadFrom(b)
return
}
func (c *udpTunConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
socksAddr := gosocks5.Addr{}
if err = socksAddr.ParseFrom(addr.String()); err != nil {
return
}
header := gosocks5.UDPHeader{
Addr: &socksAddr,
}
dgram := gosocks5.UDPDatagram{
Header: &header,
Data: b,
}
dgram.Header.Rsv = uint16(len(dgram.Data))
dgram.Header.Frag = 0xff // UDP tun relay flag, used by shadowsocks
_, err = dgram.WriteTo(c.Conn)
n = len(b)
return
}
func (c *udpTunConn) Write(b []byte) (n int, err error) {
return c.WriteTo(b, c.taddr)
}
var (
DefaultBufferSize = 4096
)
type udpConn struct {
net.PacketConn
raddr net.Addr
taddr net.Addr
bufferSize int
}
func UDPConn(c net.PacketConn, bufferSize int) net.PacketConn {
return &udpConn{
PacketConn: c,
bufferSize: bufferSize,
}
}
// ReadFrom reads an UDP datagram.
// NOTE: for server side,
// the returned addr is the target address the client want to relay to.
func (c *udpConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
rbuf := bufpool.Get(c.bufferSize)
defer bufpool.Put(rbuf)
n, c.raddr, err = c.PacketConn.ReadFrom(*rbuf)
if err != nil {
return
}
socksAddr := gosocks5.Addr{}
header := gosocks5.UDPHeader{
Addr: &socksAddr,
}
hlen, err := header.ReadFrom(bytes.NewReader((*rbuf)[:n]))
if err != nil {
return
}
n = copy(b, (*rbuf)[hlen:n])
addr, err = net.ResolveUDPAddr("udp", socksAddr.String())
return
}
func (c *udpConn) Read(b []byte) (n int, err error) {
n, _, err = c.ReadFrom(b)
return
}
func (c *udpConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
wbuf := bufpool.Get(c.bufferSize)
defer bufpool.Put(wbuf)
socksAddr := gosocks5.Addr{}
if err = socksAddr.ParseFrom(addr.String()); err != nil {
return
}
header := gosocks5.UDPHeader{
Addr: &socksAddr,
}
dgram := gosocks5.UDPDatagram{
Header: &header,
Data: b,
}
buf := bytes.NewBuffer((*wbuf)[:0])
_, err = dgram.WriteTo(buf)
if err != nil {
return
}
_, err = c.PacketConn.WriteTo(buf.Bytes(), c.raddr)
n = len(b)
return
}
func (c *udpConn) Write(b []byte) (n int, err error) {
return c.WriteTo(b, c.taddr)
}
func (c *udpConn) RemoteAddr() net.Addr {
return c.raddr
}

View File

@ -1,18 +0,0 @@
package socks
const (
// MethodTLS is an extended SOCKS5 method with tls encryption support.
MethodTLS uint8 = 0x80
// MethodTLSAuth is an extended SOCKS5 method with tls encryption and authentication support.
MethodTLSAuth uint8 = 0x82
// MethodMux is an extended SOCKS5 method for stream multiplexing.
MethodMux = 0x88
)
const (
// CmdMuxBind is an extended SOCKS5 request CMD for
// multiplexing transport with the binding server.
CmdMuxBind uint8 = 0xF2
// CmdUDPTun is an extended SOCKS5 request CMD for UDP over TCP.
CmdUDPTun uint8 = 0xF3
)

View File

@ -1,32 +0,0 @@
package util
import (
"net"
"time"
)
const (
defaultKeepAlivePeriod = 180 * time.Second
)
// TCPKeepAliveListener is a TCP listener with keep alive enabled.
type TCPKeepAliveListener struct {
KeepAlivePeriod time.Duration
*net.TCPListener
}
func (l *TCPKeepAliveListener) Accept() (c net.Conn, err error) {
tc, err := l.AcceptTCP()
if err != nil {
return
}
tc.SetKeepAlive(true)
period := l.KeepAlivePeriod
if period <= 0 {
period = defaultKeepAlivePeriod
}
tc.SetKeepAlivePeriod(period)
return tc, nil
}

View File

@ -1,44 +0,0 @@
package listener
import (
"errors"
"net"
"github.com/go-gost/gost/v3/pkg/metadata"
)
var (
ErrClosed = errors.New("accpet on closed listener")
)
// Listener is a server listener, just like a net.Listener.
type Listener interface {
Init(metadata.Metadata) error
Accept() (net.Conn, error)
Addr() net.Addr
Close() error
}
type AcceptError struct {
err error
}
func NewAcceptError(err error) error {
return &AcceptError{err: err}
}
func (e *AcceptError) Error() string {
return e.err.Error()
}
func (e *AcceptError) Timeout() bool {
return false
}
func (e *AcceptError) Temporary() bool {
return true
}
func (e *AcceptError) Unwrap() error {
return e.err
}

View File

@ -1,72 +0,0 @@
package listener
import (
"crypto/tls"
"net/url"
"github.com/go-gost/gost/v3/pkg/admission"
"github.com/go-gost/gost/v3/pkg/auth"
"github.com/go-gost/gost/v3/pkg/chain"
"github.com/go-gost/gost/v3/pkg/logger"
)
type Options struct {
Addr string
Auther auth.Authenticator
Auth *url.Userinfo
TLSConfig *tls.Config
Admission admission.Admission
Chain chain.Chainer
Logger logger.Logger
Service string
}
type Option func(opts *Options)
func AddrOption(addr string) Option {
return func(opts *Options) {
opts.Addr = addr
}
}
func AutherOption(auther auth.Authenticator) Option {
return func(opts *Options) {
opts.Auther = auther
}
}
func AuthOption(auth *url.Userinfo) Option {
return func(opts *Options) {
opts.Auth = auth
}
}
func TLSConfigOption(tlsConfig *tls.Config) Option {
return func(opts *Options) {
opts.TLSConfig = tlsConfig
}
}
func AdmissionOption(admission admission.Admission) Option {
return func(opts *Options) {
opts.Admission = admission
}
}
func ChainOption(chain chain.Chainer) Option {
return func(opts *Options) {
opts.Chain = chain
}
}
func LoggerOption(logger logger.Logger) Option {
return func(opts *Options) {
opts.Logger = logger
}
}
func ServiceOption(service string) Option {
return func(opts *Options) {
opts.Service = service
}
}

View File

@ -1,102 +0,0 @@
package rtcp
import (
"context"
"net"
"github.com/go-gost/gost/v3/pkg/chain"
"github.com/go-gost/gost/v3/pkg/connector"
"github.com/go-gost/gost/v3/pkg/listener"
"github.com/go-gost/gost/v3/pkg/logger"
md "github.com/go-gost/gost/v3/pkg/metadata"
"github.com/go-gost/gost/v3/pkg/registry"
metrics "github.com/go-gost/metrics/wrapper"
)
func init() {
registry.ListenerRegistry().Register("rtcp", NewListener)
}
type rtcpListener struct {
laddr net.Addr
ln net.Listener
md metadata
router *chain.Router
logger logger.Logger
closed chan struct{}
options listener.Options
}
func NewListener(opts ...listener.Option) listener.Listener {
options := listener.Options{}
for _, opt := range opts {
opt(&options)
}
return &rtcpListener{
closed: make(chan struct{}),
logger: options.Logger,
options: options,
}
}
func (l *rtcpListener) Init(md md.Metadata) (err error) {
if err = l.parseMetadata(md); err != nil {
return
}
laddr, err := net.ResolveTCPAddr("tcp", l.options.Addr)
if err != nil {
return
}
l.laddr = laddr
l.router = (&chain.Router{}).
WithChain(l.options.Chain).
WithLogger(l.logger)
return
}
func (l *rtcpListener) Accept() (conn net.Conn, err error) {
select {
case <-l.closed:
return nil, net.ErrClosed
default:
}
if l.ln == nil {
l.ln, err = l.router.Bind(
context.Background(), "tcp", l.laddr.String(),
connector.MuxBindOption(true),
)
if err != nil {
return nil, listener.NewAcceptError(err)
}
l.ln = metrics.WrapListener(l.options.Service, l.ln)
}
conn, err = l.ln.Accept()
if err != nil {
l.ln.Close()
l.ln = nil
return nil, listener.NewAcceptError(err)
}
return
}
func (l *rtcpListener) Addr() net.Addr {
return l.laddr
}
func (l *rtcpListener) Close() error {
select {
case <-l.closed:
default:
close(l.closed)
if l.ln != nil {
l.ln.Close()
l.ln = nil
}
}
return nil
}

View File

@ -1,19 +0,0 @@
package rtcp
import (
"time"
mdata "github.com/go-gost/gost/v3/pkg/metadata"
)
const (
defaultKeepAlivePeriod = 180 * time.Second
defaultBacklog = 128
)
type metadata struct {
}
func (l *rtcpListener) parseMetadata(md mdata.Metadata) (err error) {
return
}

View File

@ -1,109 +0,0 @@
package rudp
import (
"context"
"net"
"github.com/go-gost/gost/v3/pkg/chain"
"github.com/go-gost/gost/v3/pkg/connector"
"github.com/go-gost/gost/v3/pkg/listener"
"github.com/go-gost/gost/v3/pkg/logger"
md "github.com/go-gost/gost/v3/pkg/metadata"
"github.com/go-gost/gost/v3/pkg/registry"
metrics "github.com/go-gost/metrics/wrapper"
)
func init() {
registry.ListenerRegistry().Register("rudp", NewListener)
}
type rudpListener struct {
laddr net.Addr
ln net.Listener
router *chain.Router
closed chan struct{}
logger logger.Logger
md metadata
options listener.Options
}
func NewListener(opts ...listener.Option) listener.Listener {
options := listener.Options{}
for _, opt := range opts {
opt(&options)
}
return &rudpListener{
closed: make(chan struct{}),
logger: options.Logger,
options: options,
}
}
func (l *rudpListener) Init(md md.Metadata) (err error) {
if err = l.parseMetadata(md); err != nil {
return
}
laddr, err := net.ResolveUDPAddr("udp", l.options.Addr)
if err != nil {
return
}
l.laddr = laddr
l.router = (&chain.Router{}).
WithChain(l.options.Chain).
WithLogger(l.logger)
return
}
func (l *rudpListener) Accept() (conn net.Conn, err error) {
select {
case <-l.closed:
return nil, net.ErrClosed
default:
}
if l.ln == nil {
l.ln, err = l.router.Bind(
context.Background(), "udp", l.laddr.String(),
connector.BacklogBindOption(l.md.backlog),
connector.UDPConnTTLBindOption(l.md.ttl),
connector.UDPDataBufferSizeBindOption(l.md.readBufferSize),
connector.UDPDataQueueSizeBindOption(l.md.readQueueSize),
)
if err != nil {
return nil, listener.NewAcceptError(err)
}
}
conn, err = l.ln.Accept()
if err != nil {
l.ln.Close()
l.ln = nil
return nil, listener.NewAcceptError(err)
}
if pc, ok := conn.(net.PacketConn); ok {
conn = metrics.WrapUDPConn(l.options.Service, pc)
}
return
}
func (l *rudpListener) Addr() net.Addr {
return l.laddr
}
func (l *rudpListener) Close() error {
select {
case <-l.closed:
default:
close(l.closed)
if l.ln != nil {
l.ln.Close()
l.ln = nil
}
}
return nil
}

View File

@ -1,54 +0,0 @@
package rudp
import (
"time"
mdata "github.com/go-gost/gost/v3/pkg/metadata"
)
const (
defaultTTL = 5 * time.Second
defaultReadBufferSize = 1024
defaultReadQueueSize = 128
defaultBacklog = 128
)
type metadata struct {
ttl time.Duration
readBufferSize int
readQueueSize int
backlog int
retryCount int
}
func (l *rudpListener) parseMetadata(md mdata.Metadata) (err error) {
const (
ttl = "ttl"
readBufferSize = "readBufferSize"
readQueueSize = "readQueueSize"
backlog = "backlog"
retryCount = "retry"
)
l.md.ttl = mdata.GetDuration(md, ttl)
if l.md.ttl <= 0 {
l.md.ttl = defaultTTL
}
l.md.readBufferSize = mdata.GetInt(md, readBufferSize)
if l.md.readBufferSize <= 0 {
l.md.readBufferSize = defaultReadBufferSize
}
l.md.readQueueSize = mdata.GetInt(md, readQueueSize)
if l.md.readQueueSize <= 0 {
l.md.readQueueSize = defaultReadQueueSize
}
l.md.backlog = mdata.GetInt(md, backlog)
if l.md.backlog <= 0 {
l.md.backlog = defaultBacklog
}
l.md.retryCount = mdata.GetInt(md, retryCount)
return
}

View File

@ -1,60 +0,0 @@
package tcp
import (
"net"
"github.com/go-gost/gost/v3/pkg/listener"
"github.com/go-gost/gost/v3/pkg/logger"
md "github.com/go-gost/gost/v3/pkg/metadata"
"github.com/go-gost/gost/v3/pkg/registry"
metrics "github.com/go-gost/metrics/wrapper"
)
func init() {
registry.ListenerRegistry().Register("tcp", NewListener)
}
type tcpListener struct {
ln net.Listener
logger logger.Logger
md metadata
options listener.Options
}
func NewListener(opts ...listener.Option) listener.Listener {
options := listener.Options{}
for _, opt := range opts {
opt(&options)
}
return &tcpListener{
logger: options.Logger,
options: options,
}
}
func (l *tcpListener) Init(md md.Metadata) (err error) {
if err = l.parseMetadata(md); err != nil {
return
}
ln, err := net.Listen("tcp", l.options.Addr)
if err != nil {
return
}
l.ln = metrics.WrapListener(l.options.Service, ln)
return
}
func (l *tcpListener) Accept() (conn net.Conn, err error) {
return l.ln.Accept()
}
func (l *tcpListener) Addr() net.Addr {
return l.ln.Addr()
}
func (l *tcpListener) Close() error {
return l.ln.Close()
}

View File

@ -1,12 +0,0 @@
package tcp
import (
md "github.com/go-gost/gost/v3/pkg/metadata"
)
type metadata struct {
}
func (l *tcpListener) parseMetadata(md md.Metadata) (err error) {
return
}

View File

@ -1,64 +0,0 @@
package tls
import (
"crypto/tls"
"net"
admission "github.com/go-gost/gost/v3/pkg/admission/wrapper"
"github.com/go-gost/gost/v3/pkg/listener"
"github.com/go-gost/gost/v3/pkg/logger"
md "github.com/go-gost/gost/v3/pkg/metadata"
"github.com/go-gost/gost/v3/pkg/registry"
metrics "github.com/go-gost/metrics/wrapper"
)
func init() {
registry.ListenerRegistry().Register("tls", NewListener)
}
type tlsListener struct {
ln net.Listener
logger logger.Logger
md metadata
options listener.Options
}
func NewListener(opts ...listener.Option) listener.Listener {
options := listener.Options{}
for _, opt := range opts {
opt(&options)
}
return &tlsListener{
logger: options.Logger,
options: options,
}
}
func (l *tlsListener) Init(md md.Metadata) (err error) {
if err = l.parseMetadata(md); err != nil {
return
}
ln, err := net.Listen("tcp", l.options.Addr)
if err != nil {
return
}
ln = metrics.WrapListener(l.options.Service, ln)
ln = admission.WrapListener(l.options.Admission, ln)
l.ln = tls.NewListener(ln, l.options.TLSConfig)
return
}
func (l *tlsListener) Accept() (conn net.Conn, err error) {
return l.ln.Accept()
}
func (l *tlsListener) Addr() net.Addr {
return l.ln.Addr()
}
func (l *tlsListener) Close() error {
return l.ln.Close()
}

View File

@ -1,12 +0,0 @@
package tls
import (
mdata "github.com/go-gost/gost/v3/pkg/metadata"
)
type metadata struct {
}
func (l *tlsListener) parseMetadata(md mdata.Metadata) (err error) {
return
}

View File

@ -1,73 +0,0 @@
package udp
import (
"net"
"github.com/go-gost/gost/v3/pkg/common/util/udp"
"github.com/go-gost/gost/v3/pkg/listener"
"github.com/go-gost/gost/v3/pkg/logger"
md "github.com/go-gost/gost/v3/pkg/metadata"
"github.com/go-gost/gost/v3/pkg/registry"
metrics "github.com/go-gost/metrics/wrapper"
)
func init() {
registry.ListenerRegistry().Register("udp", NewListener)
}
type udpListener struct {
ln net.Listener
logger logger.Logger
md metadata
options listener.Options
}
func NewListener(opts ...listener.Option) listener.Listener {
options := listener.Options{}
for _, opt := range opts {
opt(&options)
}
return &udpListener{
logger: options.Logger,
options: options,
}
}
func (l *udpListener) Init(md md.Metadata) (err error) {
if err = l.parseMetadata(md); err != nil {
return
}
laddr, err := net.ResolveUDPAddr("udp", l.options.Addr)
if err != nil {
return
}
var conn net.PacketConn
conn, err = net.ListenUDP("udp", laddr)
if err != nil {
return
}
conn = metrics.WrapPacketConn(l.options.Service, conn)
l.ln = udp.NewListener(
conn,
laddr,
l.md.backlog,
l.md.readQueueSize, l.md.readBufferSize,
l.md.ttl,
l.logger)
return
}
func (l *udpListener) Accept() (conn net.Conn, err error) {
return l.ln.Accept()
}
func (l *udpListener) Addr() net.Addr {
return l.ln.Addr()
}
func (l *udpListener) Close() error {
return l.ln.Close()
}

View File

@ -1,52 +0,0 @@
package udp
import (
"time"
mdata "github.com/go-gost/gost/v3/pkg/metadata"
)
const (
defaultTTL = 5 * time.Second
defaultReadBufferSize = 1024
defaultReadQueueSize = 128
defaultBacklog = 128
)
type metadata struct {
ttl time.Duration
readBufferSize int
readQueueSize int
backlog int
}
func (l *udpListener) parseMetadata(md mdata.Metadata) (err error) {
const (
ttl = "ttl"
readBufferSize = "readBufferSize"
readQueueSize = "readQueueSize"
backlog = "backlog"
)
l.md.ttl = mdata.GetDuration(md, ttl)
if l.md.ttl <= 0 {
l.md.ttl = defaultTTL
}
l.md.readBufferSize = mdata.GetInt(md, readBufferSize)
if l.md.readBufferSize <= 0 {
l.md.readBufferSize = defaultReadBufferSize
}
l.md.readQueueSize = mdata.GetInt(md, readQueueSize)
if l.md.readQueueSize <= 0 {
l.md.readQueueSize = defaultReadQueueSize
}
l.md.backlog = mdata.GetInt(md, backlog)
if l.md.backlog <= 0 {
l.md.backlog = defaultBacklog
}
return
}

View File

@ -1,155 +0,0 @@
package logger
import (
"fmt"
"path/filepath"
"runtime"
"github.com/sirupsen/logrus"
)
var (
defaultLogger = NewLogger()
)
func Default() Logger {
return defaultLogger
}
func SetDefault(logger Logger) {
defaultLogger = logger
}
type logger struct {
logger *logrus.Entry
}
func NewLogger(opts ...LoggerOption) Logger {
var options LoggerOptions
for _, opt := range opts {
opt(&options)
}
log := logrus.New()
if options.Output != nil {
log.SetOutput(options.Output)
}
switch options.Format {
case TextFormat:
log.SetFormatter(&logrus.TextFormatter{
FullTimestamp: true,
})
default:
log.SetFormatter(&logrus.JSONFormatter{
DisableHTMLEscape: true,
// PrettyPrint: true,
})
}
switch options.Level {
case DebugLevel, InfoLevel, WarnLevel, ErrorLevel, FatalLevel:
lvl, _ := logrus.ParseLevel(string(options.Level))
log.SetLevel(lvl)
default:
log.SetLevel(logrus.InfoLevel)
}
return &logger{
logger: logrus.NewEntry(log),
}
}
// WithFields adds new fields to log.
func (l *logger) WithFields(fields map[string]any) Logger {
return &logger{
logger: l.logger.WithFields(logrus.Fields(fields)),
}
}
// Debug logs a message at level Debug.
func (l *logger) Debug(args ...any) {
l.log(logrus.DebugLevel, args...)
}
// Debugf logs a message at level Debug.
func (l *logger) Debugf(format string, args ...any) {
l.logf(logrus.DebugLevel, format, args...)
}
// Info logs a message at level Info.
func (l *logger) Info(args ...any) {
l.log(logrus.InfoLevel, args...)
}
// Infof logs a message at level Info.
func (l *logger) Infof(format string, args ...any) {
l.logf(logrus.InfoLevel, format, args...)
}
// Warn logs a message at level Warn.
func (l *logger) Warn(args ...any) {
l.log(logrus.WarnLevel, args...)
}
// Warnf logs a message at level Warn.
func (l *logger) Warnf(format string, args ...any) {
l.logf(logrus.WarnLevel, format, args...)
}
// Error logs a message at level Error.
func (l *logger) Error(args ...any) {
l.log(logrus.ErrorLevel, args...)
}
// Errorf logs a message at level Error.
func (l *logger) Errorf(format string, args ...any) {
l.logf(logrus.ErrorLevel, format, args...)
}
// Fatal logs a message at level Fatal then the process will exit with status set to 1.
func (l *logger) Fatal(args ...any) {
l.log(logrus.FatalLevel, args...)
l.logger.Logger.Exit(1)
}
// Fatalf logs a message at level Fatal then the process will exit with status set to 1.
func (l *logger) Fatalf(format string, args ...any) {
l.logf(logrus.FatalLevel, format, args...)
l.logger.Logger.Exit(1)
}
func (l *logger) GetLevel() LogLevel {
return LogLevel(l.logger.Logger.GetLevel().String())
}
func (l *logger) IsLevelEnabled(level LogLevel) bool {
lvl, _ := logrus.ParseLevel(string(level))
return l.logger.Logger.IsLevelEnabled(lvl)
}
func (l *logger) log(level logrus.Level, args ...any) {
lg := l.logger
if l.logger.Logger.IsLevelEnabled(logrus.DebugLevel) {
lg = lg.WithField("caller", l.caller(3))
}
lg.Log(level, args...)
}
func (l *logger) logf(level logrus.Level, format string, args ...any) {
lg := l.logger
if l.logger.Logger.IsLevelEnabled(logrus.DebugLevel) {
lg = lg.WithField("caller", l.caller(3))
}
lg.Logf(level, format, args...)
}
func (l *logger) caller(skip int) string {
_, file, line, ok := runtime.Caller(skip)
if !ok {
file = "<???>"
} else {
file = filepath.Join(filepath.Base(filepath.Dir(file)), filepath.Base(file))
}
return fmt.Sprintf("%s:%d", file, line)
}

View File

@ -1,71 +0,0 @@
package logger
import (
"io"
)
// LogFormat is format type
type LogFormat string
const (
TextFormat LogFormat = "text"
JSONFormat LogFormat = "json"
)
// LogLevel is Logger Level type
type LogLevel string
const (
// DebugLevel has verbose message
DebugLevel LogLevel = "debug"
// InfoLevel is default log level
InfoLevel LogLevel = "info"
// WarnLevel is for logging messages about possible issues
WarnLevel LogLevel = "warn"
// ErrorLevel is for logging errors
ErrorLevel LogLevel = "error"
// FatalLevel is for logging fatal messages. The system shuts down after logging the message.
FatalLevel LogLevel = "fatal"
)
type Logger interface {
WithFields(map[string]any) Logger
Debug(args ...any)
Debugf(format string, args ...any)
Info(args ...any)
Infof(format string, args ...any)
Warn(args ...any)
Warnf(format string, args ...any)
Error(args ...any)
Errorf(format string, args ...any)
Fatal(args ...any)
Fatalf(format string, args ...any)
GetLevel() LogLevel
IsLevelEnabled(level LogLevel) bool
}
type LoggerOptions struct {
Output io.Writer
Format LogFormat
Level LogLevel
}
type LoggerOption func(opts *LoggerOptions)
func OutputLoggerOption(out io.Writer) LoggerOption {
return func(opts *LoggerOptions) {
opts.Output = out
}
}
func FormatLoggerOption(format LogFormat) LoggerOption {
return func(opts *LoggerOptions) {
opts.Format = format
}
}
func LevelLoggerOption(level LogLevel) LoggerOption {
return func(opts *LoggerOptions) {
opts.Level = level
}
}

View File

@ -1,53 +0,0 @@
package logger
var (
nop = &nopLogger{}
)
func Nop() Logger {
return nop
}
type nopLogger struct{}
func (l *nopLogger) WithFields(fields map[string]any) Logger {
return l
}
func (l *nopLogger) Debug(args ...any) {
}
func (l *nopLogger) Debugf(format string, args ...any) {
}
func (l *nopLogger) Info(args ...any) {
}
func (l *nopLogger) Infof(format string, args ...any) {
}
func (l *nopLogger) Warn(args ...any) {
}
func (l *nopLogger) Warnf(format string, args ...any) {
}
func (l *nopLogger) Error(args ...any) {
}
func (l *nopLogger) Errorf(format string, args ...any) {
}
func (l *nopLogger) Fatal(args ...any) {
}
func (l *nopLogger) Fatalf(format string, args ...any) {
}
func (l *nopLogger) GetLevel() LogLevel {
return ""
}
func (l *nopLogger) IsLevelEnabled(level LogLevel) bool {
return false
}

View File

@ -1,156 +0,0 @@
package metadata
import (
"fmt"
"strconv"
"time"
)
type Metadatable interface {
GetMetadata() Metadata
}
type Metadata interface {
IsExists(key string) bool
Set(key string, value any)
Get(key string) any
}
type MapMetadata map[string]any
func (m MapMetadata) IsExists(key string) bool {
_, ok := m[key]
return ok
}
func (m MapMetadata) Set(key string, value any) {
m[key] = value
}
func (m MapMetadata) Get(key string) any {
if m != nil {
return m[key]
}
return nil
}
func (m MapMetadata) Del(key string) {
delete(m, key)
}
func GetBool(md Metadata, key string) (v bool) {
if md == nil || !md.IsExists(key) {
return
}
switch vv := md.Get(key).(type) {
case bool:
return vv
case int:
return vv != 0
case string:
v, _ = strconv.ParseBool(vv)
return
}
return
}
func GetInt(md Metadata, key string) (v int) {
if md == nil {
return
}
switch vv := md.Get(key).(type) {
case bool:
if vv {
v = 1
}
case int:
return vv
case string:
v, _ = strconv.Atoi(vv)
return
}
return
}
func GetFloat(md Metadata, key string) (v float64) {
if md == nil {
return
}
switch vv := md.Get(key).(type) {
case int:
return float64(vv)
case string:
v, _ = strconv.ParseFloat(vv, 64)
return
}
return
}
func GetDuration(md Metadata, key string) (v time.Duration) {
if md == nil {
return
}
switch vv := md.Get(key).(type) {
case int:
return time.Duration(vv) * time.Second
case string:
v, _ = time.ParseDuration(vv)
if v == 0 {
n, _ := strconv.Atoi(vv)
v = time.Duration(n) * time.Second
}
}
return
}
func GetString(md Metadata, key string) (v string) {
if md != nil {
v, _ = md.Get(key).(string)
}
return
}
func GetStrings(md Metadata, key string) (ss []string) {
switch v := md.Get(key).(type) {
case []string:
ss = v
case []any:
for _, vv := range v {
if s, ok := vv.(string); ok {
ss = append(ss, s)
}
}
}
return
}
func GetStringMap(md Metadata, key string) (m map[string]any) {
switch vv := md.Get(key).(type) {
case map[string]any:
return vv
case map[any]any:
m = make(map[string]any)
for k, v := range vv {
m[fmt.Sprintf("%v", k)] = v
}
}
return
}
func GetStringMapString(md Metadata, key string) (m map[string]string) {
switch vv := md.Get(key).(type) {
case map[string]any:
m = make(map[string]string)
for k, v := range vv {
m[k] = fmt.Sprintf("%v", v)
}
case map[any]any:
m = make(map[string]string)
for k, v := range vv {
m[fmt.Sprintf("%v", k)] = fmt.Sprintf("%v", v)
}
}
return
}

View File

@ -1,40 +0,0 @@
package registry
import (
"github.com/go-gost/gost/v3/pkg/admission"
)
type admissionRegistry struct {
registry
}
func (r *admissionRegistry) Register(name string, v admission.Admission) error {
return r.registry.Register(name, v)
}
func (r *admissionRegistry) Get(name string) admission.Admission {
if name != "" {
return &admissionWrapper{name: name, r: r}
}
return nil
}
func (r *admissionRegistry) get(name string) admission.Admission {
if v := r.registry.Get(name); v != nil {
return v.(admission.Admission)
}
return nil
}
type admissionWrapper struct {
name string
r *admissionRegistry
}
func (w *admissionWrapper) Admit(addr string) bool {
p := w.r.get(w.name)
if p == nil {
return false
}
return p.Admit(addr)
}

View File

@ -1,40 +0,0 @@
package registry
import (
"github.com/go-gost/gost/v3/pkg/auth"
)
type autherRegistry struct {
registry
}
func (r *autherRegistry) Register(name string, v auth.Authenticator) error {
return r.registry.Register(name, v)
}
func (r *autherRegistry) Get(name string) auth.Authenticator {
if name != "" {
return &autherWrapper{name: name, r: r}
}
return nil
}
func (r *autherRegistry) get(name string) auth.Authenticator {
if v := r.registry.Get(name); v != nil {
return v.(auth.Authenticator)
}
return nil
}
type autherWrapper struct {
name string
r *autherRegistry
}
func (w *autherWrapper) Authenticate(user, password string) bool {
v := w.r.get(w.name)
if v == nil {
return true
}
return v.Authenticate(user, password)
}

Some files were not shown because too many files have changed in this diff Show More