Compare commits

...
90 Commits
Author SHA1 Message Date
ginuerzh a69a759e17 tunnel: add multiple entrypoints 2025-10-09 22:32:03 +08:00
ginuerzh 007ff0bae9 fix panic for channel close 2025-09-07 14:20:05 +08:00
ginuerzh e2072399dd fix sniffing for websocket 2025-09-04 21:30:49 +08:00
ginuerzh c7d16962ec add service option for plugin 2025-08-29 23:36:31 +08:00
Denis Galeev a12ed27dfa fix handler/socks5: UDP IP address filtering 2025-08-28 16:54:03 +08:00
Denis Galeev bf70f07419 fix recorder: writes order 2025-08-25 10:03:26 +08:00
ginuerzh a65b7a059a try to fix http2 panic (#54) 2025-08-21 22:12:43 +08:00
Denis Galeev d5683e9138 prevent panic 2025-08-21 21:21:23 +08:00
ginuerzh 52289bea6c add network param for sniffing 2025-08-13 21:25:35 +08:00
ginuerzh d0ff5358df fix panic for nil pointer 2025-08-13 21:24:05 +08:00
ginuerzh 0e5c285eb7 fix empty hop nodes 2025-08-10 18:36:43 +08:00
ginuerzh a5f66b0615 node: add bypass and admission matcher 2025-08-10 17:59:17 +08:00
ginuerzh f3d3d2231e fix tun route for ipv6 2025-08-09 19:19:04 +08:00
ginuerzh 4eb006f1b0 tungo: debug exec.Command output 2025-08-09 16:34:04 +08:00
ginuerzh 2b9f795972 logger: fix panic when ouput is none 2025-08-09 14:03:26 +08:00
ginuerzh 589c185053 forwarder: connect to sni host for empty node 2025-08-09 12:43:09 +08:00
ginuerzh 404aaae5f1 without cancel the context 2025-08-05 20:24:00 +08:00
ginuerzh fd9dc6408a fix ws listener 2025-08-05 00:13:48 +08:00
ginuerzh b597467858 add context for conn 2025-08-04 19:29:38 +08:00
ginuerzh ad5cf6fd61 add proxyProtocol support for dialer 2025-08-03 15:36:29 +08:00
ginuerzh f71351f5ef add interface xnet.SrcAddr/DstAddr 2025-08-03 10:15:53 +08:00
ginuerzh 79203e407c add scoped options for cmd 2025-08-02 12:07:18 +08:00
ginuerzh db21de831a fix xnet.Pipe 2025-08-01 23:00:50 +08:00
ginuerzh a5309eee97 add buffer size option for udp connection 2025-07-30 21:40:53 +08:00
ginuerzh b64e5901b6 http: fix response for connect request 2025-07-30 21:40:31 +08:00
ginuerzh 87e454a6f1 tungo: additional metadata options 2025-07-30 10:07:18 +08:00
ginuerzh 3bbc10796c http2: added non-connect request support 2025-07-30 10:06:52 +08:00
ginuerzh 208d18125c replace xnet.Transport by xnet.Pipe 2025-07-28 20:52:15 +08:00
ginuerzh 35a049fb03 rename vtun to tungo 2025-07-26 16:17:04 +08:00
ginuerzh 281295d02f fix tun device 2025-07-23 22:44:02 +08:00
ginuerzh acae52ab89 add vtun listener and handler 2025-07-23 21:30:21 +08:00
ginuerzh afcf4a5252 parse real client IP 2025-07-15 19:59:10 +08:00
ginuerzh d9a2f44a78 add src field for logger and recorder 2025-07-02 21:35:02 +08:00
ginuerzh 97ed5080a6 service: reset tempDelay when retrying after accept error #52 2025-06-28 18:11:13 +08:00
ginuerzh c8d905dea5 read config from stdin 2025-06-28 18:09:04 +08:00
ginuerzh fe096d6e74 add clientIP for ws logger #51 2025-06-28 18:02:21 +08:00
ginuerzh 3ec2f9d487 update quic 2025-06-25 21:06:23 +08:00
ginuerzh ce3c28baf5 rm deprecated library shadowsocks-go/shadowsocks 2025-06-25 21:05:39 +08:00
ginuerzh 11d982e062 add new APIs 2025-06-25 21:02:55 +08:00
ginuerzh 6caad5e37e add file option for AuthConfig 2025-06-24 21:16:17 +08:00
ginuerzh d819fd3041 support http-100 continue for sniffer 2025-06-24 21:15:38 +08:00
ginuerzh 8cacfad9eb fix metrics for listener 2025-06-24 21:13:30 +08:00
dependabot[bot]andginuerzh a71b9d79fd Bump golang.org/x/net from 0.33.0 to 0.38.0 (#50)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.33.0 to 0.38.0.
- [Commits](https://github.com/golang/net/compare/v0.33.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.38.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: ginuerzh <ginuerzh@users.noreply.github.com>
2025-06-24 11:21:19 +08:00
dependabot[bot] 9a380d3fd8 Bump golang.org/x/crypto from 0.31.0 to 0.35.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.31.0 to 0.35.0.
- [Commits](https://github.com/golang/crypto/compare/v0.31.0...v0.35.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-version: 0.35.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-24 11:01:46 +08:00
Denis Galeev 4c99e11d81 fix parse 2025-06-24 11:01:26 +08:00
Denis Galeev 1eaf08c9c1 fix recorders 2025-06-24 11:01:26 +08:00
Denis Galeev 0e59ccc64b add new metric 2025-06-24 11:01:26 +08:00
ginuerzh 04d1587a77 set dns for tun 2025-02-25 20:39:33 +08:00
ginuerzh 69a0aa8853 use fixed buffer size 2025-02-18 21:04:04 +08:00
ginuerzh e0915affda use fixed buffer size 2025-02-18 17:38:27 +08:00
ginuerzh d610e2dc01 add traffic limiter for relay bind 2025-02-18 17:37:13 +08:00
ginuerzh 2495b13053 fix traffic logging for socks5 udp 2025-02-18 17:30:36 +08:00
wzxjohn 11bca5bc3d fix(http): set Proxy-Agent header after authenticate to avoid leak #42 2025-02-18 15:30:56 +08:00
Denis Galeev e4b1c3c2b5 feat recorder: add rotate options 2025-02-18 15:30:42 +08:00
ginuerzh 75a106ee84 fix tun device 2025-02-11 23:12:46 +08:00
ginuerzh 2132268780 Bump golang.zx2c4.com/wireguard 2025-02-11 22:13:57 +08:00
ginuerzh ef440e8b4a router: router.id is optional 2025-02-11 20:54:52 +08:00
ginuerzh fc12e33786 router handler: add cache for sd service 2025-02-09 16:16:44 +08:00
ginuerzh 08ad260606 add entrypoint for router handler 2025-02-08 23:07:54 +08:00
ginuerzh 9cd03b6b59 update go.mod 2025-02-07 15:45:14 +08:00
ginuerzh 355c72477a add router handler & connector 2025-02-07 15:44:31 +08:00
ginuerzh 379d9a73ad service: add limiter.scope option 2025-01-20 23:22:58 +08:00
ginuerzh bf36bdc680 update go.mod 2025-01-10 21:48:05 +08:00
ginuerzh aa7528fc1e add reload for web api 2025-01-10 21:39:21 +08:00
ginuerzh e480039691 update go.mod 2025-01-08 23:12:56 +08:00
ginuerzh 489a762811 service graceful shutdown 2025-01-08 23:12:06 +08:00
ginuerzh eef3bd7f25 fix panic for stats wrapper 2024-12-27 20:42:20 +08:00
ginuerzh 06d993023f add traffic reset for stats 2024-12-24 20:11:22 +08:00
TimWhite 914a4622fd Update dialer.go 2024-12-24 17:22:13 +08:00
ginuerzh a5d4774682 add ip range matcher for bypass 2024-12-23 21:06:55 +08:00
ginuerzh be8e0555e5 handle tls for tunnel entrypoint 2024-12-23 21:05:55 +08:00
ginuerzh 2693ba61c4 forwarder: fix ip matcher for tls 2024-12-18 19:05:19 +08:00
ginuerzh fcb54bd576 fix observer for http2 2024-12-16 20:48:11 +08:00
dependabot[bot] 5206d1bd58 Bump golang.org/x/crypto from 0.30.0 to 0.31.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.30.0 to 0.31.0.
- [Commits](https://github.com/golang/crypto/compare/v0.30.0...v0.31.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-16 19:09:33 +08:00
ginuerzh 98f5a5f7eb fix webtransport 2024-12-11 22:25:51 +08:00
zhangchunyun ff6807815f consider byte order for IPv6 original dst address 2024-12-11 22:00:03 +08:00
dependabot[bot] 8d962398ab Bump github.com/quic-go/quic-go from 0.45.0 to 0.48.2
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.45.0 to 0.48.2.
- [Release notes](https://github.com/quic-go/quic-go/releases)
- [Changelog](https://github.com/quic-go/quic-go/blob/master/Changelog.md)
- [Commits](https://github.com/quic-go/quic-go/compare/v0.45.0...v0.48.2)

---
updated-dependencies:
- dependency-name: github.com/quic-go/quic-go
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-11 21:57:24 +08:00
ginuerzh c1e6164a83 add unix domain socket support for api & metrics services 2024-11-22 22:49:10 +08:00
ginuerzh 6f001a779a tunnel: fix tunnel routing for public entrypoint 2024-11-22 22:04:44 +08:00
ginuerzh 2e8a5d6f51 fix http keepalive for forwarding 2024-11-17 16:16:20 +08:00
ginuerzh c2aedbabd9 add routing rule for node 2024-11-15 20:28:47 +08:00
ginuerzh 79b6b9138e add limiterRefreshInterval option for limiter 2024-11-14 20:16:09 +08:00
ginuerzh 3db20563d2 add custom http response header for reverse proxy 2024-11-07 11:04:42 +08:00
ginuerzh a12870e766 add compression flag option for http request 2024-11-02 20:41:23 +08:00
ginuerzh edb9a992e2 http: fix http/1.0 connection close 2024-11-01 18:17:50 +08:00
ginuerzh f4d9a5f79b tunnel: checking closed connector 2024-11-01 15:25:45 +08:00
ginuerzh 88ba01b3dd add redirect for handler recorder object 2024-10-31 19:14:21 +08:00
ginuerzh d8126e8e30 update go.mod 2024-10-22 22:54:04 +08:00
ginuerzh a97ac1f30a fix http body recorder 2024-10-22 22:51:33 +08:00
ginuerzh 3b4fd643de fix udp port forwarding 2024-10-22 22:50:40 +08:00
305 changed files with 12965 additions and 3337 deletions
+47 -18
View File
@@ -13,6 +13,7 @@ import (
"github.com/go-gost/core/logger"
"github.com/go-gost/x/internal/loader"
"github.com/go-gost/x/internal/matcher"
xlogger "github.com/go-gost/x/logger"
)
type options struct {
@@ -75,6 +76,7 @@ type localAdmission struct {
mu sync.RWMutex
cancelFunc context.CancelFunc
options options
logger logger.Logger
}
// NewAdmission creates and initializes a new Admission using matcher patterns as its match rules.
@@ -85,18 +87,19 @@ func NewAdmission(opts ...Option) admission.Admission {
opt(&options)
}
ctx, cancel := context.WithCancel(context.TODO())
ctx, cancel := context.WithCancel(context.Background())
p := &localAdmission{
cancelFunc: cancel,
options: options,
ipMatcher: matcher.NopMatcher(),
cidrMatcher: matcher.NopMatcher(),
cancelFunc: cancel,
options: options,
logger: options.logger,
}
if p.logger == nil {
p.logger = xlogger.Nop()
}
if err := p.reload(ctx); err != nil {
options.logger.Warnf("reload: %v", err)
}
if p.options.period > 0 {
go p.periodReload(ctx)
}
go p.periodReload(ctx)
return p
}
@@ -117,13 +120,20 @@ func (p *localAdmission) Admit(ctx context.Context, addr string, opts ...admissi
p.options.whitelist && matched
if !b {
p.options.logger.Debugf("%s is denied", addr)
p.logger.Debugf("%s is denied", addr)
}
return b
}
func (p *localAdmission) periodReload(ctx context.Context) error {
if err := p.reload(ctx); err != nil {
p.logger.Warnf("reload: %v", err)
}
period := p.options.period
if period <= 0 {
return nil
}
if period < time.Second {
period = time.Second
}
@@ -134,7 +144,7 @@ func (p *localAdmission) periodReload(ctx context.Context) error {
select {
case <-ticker.C:
if err := p.reload(ctx); err != nil {
p.options.logger.Warnf("reload: %v", err)
p.logger.Warnf("reload: %v", err)
// return err
}
case <-ctx.Done():
@@ -162,7 +172,7 @@ func (p *localAdmission) reload(ctx context.Context) error {
continue
}
if ipAddr, _ := net.ResolveIPAddr("ip", pattern); ipAddr != nil {
p.options.logger.Debugf("resolve IP: %s -> %s", pattern, ipAddr)
p.logger.Debugf("resolve IP: %s -> %s", pattern, ipAddr)
ips = append(ips, ipAddr.IP)
}
}
@@ -181,7 +191,7 @@ func (p *localAdmission) load(ctx context.Context) (patterns []string, err error
if lister, ok := p.options.fileLoader.(loader.Lister); ok {
list, er := lister.List(ctx)
if er != nil {
p.options.logger.Warnf("file loader: %v", er)
p.logger.Warnf("file loader: %v", er)
}
for _, s := range list {
if line := p.parseLine(s); line != "" {
@@ -191,7 +201,7 @@ func (p *localAdmission) load(ctx context.Context) (patterns []string, err error
} else {
r, er := p.options.fileLoader.Load(ctx)
if er != nil {
p.options.logger.Warnf("file loader: %v", er)
p.logger.Warnf("file loader: %v", er)
}
if v, _ := p.parsePatterns(r); v != nil {
patterns = append(patterns, v...)
@@ -202,13 +212,13 @@ func (p *localAdmission) load(ctx context.Context) (patterns []string, err error
if lister, ok := p.options.redisLoader.(loader.Lister); ok {
list, er := lister.List(ctx)
if er != nil {
p.options.logger.Warnf("redis loader: %v", er)
p.logger.Warnf("redis loader: %v", er)
}
patterns = append(patterns, list...)
} else {
r, er := p.options.redisLoader.Load(ctx)
if er != nil {
p.options.logger.Warnf("redis loader: %v", er)
p.logger.Warnf("redis loader: %v", er)
}
if v, _ := p.parsePatterns(r); v != nil {
patterns = append(patterns, v...)
@@ -219,14 +229,14 @@ func (p *localAdmission) load(ctx context.Context) (patterns []string, err error
if p.options.httpLoader != nil {
r, er := p.options.httpLoader.Load(ctx)
if er != nil {
p.options.logger.Warnf("http loader: %v", er)
p.logger.Warnf("http loader: %v", er)
}
if v, _ := p.parsePatterns(r); v != nil {
patterns = append(patterns, v...)
}
}
p.options.logger.Debugf("load items %d", len(patterns))
p.logger.Debugf("load items %d", len(patterns))
return
}
@@ -271,3 +281,22 @@ func (p *localAdmission) Close() error {
}
return nil
}
type admissionGroup struct {
admissions []admission.Admission
}
func AdmissionGroup(admissions ...admission.Admission) admission.Admission {
return &admissionGroup{
admissions: admissions,
}
}
func (p *admissionGroup) Admit(ctx context.Context, addr string, opts ...admission.Option) bool {
for _, admission := range p.admissions {
if admission != nil && !admission.Admit(ctx, addr, opts...) {
return false
}
}
return true
}
+7 -1
View File
@@ -48,9 +48,15 @@ func (p *grpcPlugin) Admit(ctx context.Context, addr string, opts ...admission.O
return false
}
var options admission.Options
for _, opt := range opts {
opt(&options)
}
r, err := p.client.Admit(ctx,
&proto.AdmissionRequest{
Addr: addr,
Service: options.Service,
Addr: addr,
})
if err != nil {
p.log.Error(err)
+9 -2
View File
@@ -12,7 +12,8 @@ import (
)
type httpPluginRequest struct {
Addr string `json:"addr"`
Service string `json:"service"`
Addr string `json:"addr"`
}
type httpPluginResponse struct {
@@ -49,8 +50,14 @@ func (p *httpPlugin) Admit(ctx context.Context, addr string, opts ...admission.O
return
}
var options admission.Options
for _, opt := range opts {
opt(&options)
}
rb := httpPluginRequest{
Addr: addr,
Service: options.Service,
Addr: addr,
}
v, err := json.Marshal(&rb)
if err != nil {
+25 -10
View File
@@ -8,7 +8,8 @@ import (
"syscall"
"github.com/go-gost/core/admission"
"github.com/go-gost/core/metadata"
"github.com/go-gost/x/ctx"
xio "github.com/go-gost/x/internal/io"
xnet "github.com/go-gost/x/internal/net"
"github.com/go-gost/x/internal/net/udp"
)
@@ -50,9 +51,23 @@ func (c *serverConn) SyscallConn() (rc syscall.RawConn, err error) {
return
}
func (c *serverConn) Metadata() metadata.Metadata {
if md, ok := c.Conn.(metadata.Metadatable); ok {
return md.Metadata()
func (c *serverConn) CloseRead() error {
if sc, ok := c.Conn.(xio.CloseRead); ok {
return sc.CloseRead()
}
return xio.ErrUnsupported
}
func (c *serverConn) CloseWrite() error {
if sc, ok := c.Conn.(xio.CloseWrite); ok {
return sc.CloseWrite()
}
return xio.ErrUnsupported
}
func (c *serverConn) Context() context.Context {
if innerCtx, ok := c.Conn.(ctx.Context); ok {
return innerCtx.Context()
}
return nil
}
@@ -88,9 +103,9 @@ func (c *packetConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
}
}
func (c *packetConn) Metadata() metadata.Metadata {
if md, ok := c.PacketConn.(metadata.Metadatable); ok {
return md.Metadata()
func (c *packetConn) Context() context.Context {
if innerCtx, ok := c.PacketConn.(ctx.Context); ok {
return innerCtx.Context()
}
return nil
}
@@ -234,9 +249,9 @@ func (c *udpConn) SetDSCP(n int) error {
return nil
}
func (c *udpConn) Metadata() metadata.Metadata {
if md, ok := c.PacketConn.(metadata.Metadatable); ok {
return md.Metadata()
func (c *udpConn) Context() context.Context {
if innerCtx, ok := c.PacketConn.(ctx.Context); ok {
return innerCtx.Context()
}
return nil
}
+18 -2
View File
@@ -6,19 +6,22 @@ import (
"github.com/go-gost/core/admission"
"github.com/go-gost/core/logger"
xctx "github.com/go-gost/x/ctx"
)
type listener struct {
service string
net.Listener
admission admission.Admission
log logger.Logger
}
func WrapListener(admission admission.Admission, ln net.Listener) net.Listener {
func WrapListener(service string, admission admission.Admission, ln net.Listener) net.Listener {
if admission == nil {
return ln
}
return &listener{
service: service,
Listener: ln,
admission: admission,
}
@@ -30,8 +33,21 @@ func (ln *listener) Accept() (net.Conn, error) {
if err != nil {
return nil, err
}
ctx := context.Background()
if cc, ok := c.(xctx.Context); ok {
if cv := cc.Context(); cv != nil {
ctx = cv
}
}
clientAddr := c.RemoteAddr()
if addr := xctx.SrcAddrFromContext(ctx); addr != nil {
clientAddr = addr
}
if ln.admission != nil &&
!ln.admission.Admit(context.Background(), c.RemoteAddr().String()) {
!ln.admission.Admit(ctx, clientAddr.String(), admission.WithService(ln.service)) {
c.Close()
continue
}
+50 -82
View File
@@ -1,14 +1,14 @@
// swagger generate spec -o swagger.yaml
package api
import (
"embed"
"net"
"net/http"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"github.com/go-gost/core/auth"
"github.com/go-gost/core/service"
)
var (
@@ -17,56 +17,22 @@ var (
)
type Response struct {
Code int `json:"code,omitempty"`
Msg string `json:"msg,omitempty"`
Code int `json:"code,omitempty"`
Msg string `json:"msg,omitempty"`
Data interface{} `json:"data,omitempty"`
}
type options struct {
accessLog bool
pathPrefix string
auther auth.Authenticator
type Options struct {
AccessLog bool
PathPrefix string
Auther auth.Authenticator
}
type Option func(*options)
func PathPrefixOption(pathPrefix string) Option {
return func(o *options) {
o.pathPrefix = pathPrefix
}
}
func AccessLogOption(enable bool) Option {
return func(o *options) {
o.accessLog = enable
}
}
func AutherOption(auther auth.Authenticator) Option {
return func(o *options) {
o.auther = auther
}
}
type server struct {
s *http.Server
ln net.Listener
cclose chan struct{}
}
func NewService(addr string, opts ...Option) (service.Service, error) {
ln, err := net.Listen("tcp", addr)
if err != nil {
return nil, err
func Register(r *gin.Engine, opts *Options) {
if opts == nil {
opts = &Options{}
}
var options options
for _, opt := range opts {
opt(&options)
}
gin.SetMode(gin.ReleaseMode)
r := gin.New()
r.Use(
cors.New((cors.Config{
AllowAllOrigins: true,
@@ -76,115 +42,117 @@ func NewService(addr string, opts ...Option) (service.Service, error) {
})),
gin.Recovery(),
)
if options.accessLog {
if opts.AccessLog {
r.Use(mwLogger())
}
router := r.Group("")
if options.pathPrefix != "" {
router = router.Group(options.pathPrefix)
if opts.PathPrefix != "" {
router = router.Group(opts.PathPrefix)
}
router.StaticFS("/docs", http.FS(swaggerDoc))
config := router.Group("/config")
config.Use(mwBasicAuth(options.auther))
registerConfig(config)
config.Use(mwBasicAuth(opts.Auther))
return &server{
s: &http.Server{
Handler: r,
},
ln: ln,
cclose: make(chan struct{}),
}, nil
}
func (s *server) Serve() error {
return s.s.Serve(s.ln)
}
func (s *server) Addr() net.Addr {
return s.ln.Addr()
}
func (s *server) Close() error {
return s.s.Close()
}
func (s *server) IsClosed() bool {
select {
case <-s.cclose:
return true
default:
return false
}
}
func registerConfig(config *gin.RouterGroup) {
config.GET("", getConfig)
config.POST("", saveConfig)
config.POST("/reload", reloadConfig)
config.GET("/services", getServiceList)
config.GET("/services/:service", getService)
config.POST("/services", createService)
config.PUT("/services/:service", updateService)
config.DELETE("/services/:service", deleteService)
config.GET("/chains", getChainList)
config.GET("/chains/:chain", getChain)
config.POST("/chains", createChain)
config.PUT("/chains/:chain", updateChain)
config.DELETE("/chains/:chain", deleteChain)
config.GET("/hops", getHopList)
config.GET("/hops/:hop", getHop)
config.POST("/hops", createHop)
config.PUT("/hops/:hop", updateHop)
config.DELETE("/hops/:hop", deleteHop)
config.GET("/authers", getAutherList)
config.GET("/authers/:auther", getAuther)
config.POST("/authers", createAuther)
config.PUT("/authers/:auther", updateAuther)
config.DELETE("/authers/:auther", deleteAuther)
config.GET("/admissions", getAdmissionList)
config.GET("/admissions/:admission", getAdmission)
config.POST("/admissions", createAdmission)
config.PUT("/admissions/:admission", updateAdmission)
config.DELETE("/admissions/:admission", deleteAdmission)
config.GET("/bypasses", getBypassList)
config.GET("/bypasses/:bypass", getBypass)
config.POST("/bypasses", createBypass)
config.PUT("/bypasses/:bypass", updateBypass)
config.DELETE("/bypasses/:bypass", deleteBypass)
config.GET("/resolvers", getResolverList)
config.GET("/resolvers/:resolver", getResolver)
config.POST("/resolvers", createResolver)
config.PUT("/resolvers/:resolver", updateResolver)
config.DELETE("/resolvers/:resolver", deleteResolver)
config.GET("/hosts", getHostsList)
config.GET("/hosts/:hosts", getHosts)
config.POST("/hosts", createHosts)
config.PUT("/hosts/:hosts", updateHosts)
config.DELETE("/hosts/:hosts", deleteHosts)
config.GET("/ingresses", getIngressList)
config.GET("/ingresses/:ingress", getIngress)
config.POST("/ingresses", createIngress)
config.PUT("/ingresses/:ingress", updateIngress)
config.DELETE("/ingresses/:ingress", deleteIngress)
config.GET("/routers", getRouterList)
config.GET("/routers/:router", getRouter)
config.POST("/routers", createRouter)
config.PUT("/routers/:router", updateRouter)
config.DELETE("/routers/:router", deleteRouter)
config.GET("/observers", getObserverList)
config.GET("/observers/:observer", getObserver)
config.POST("/observers", createObserver)
config.PUT("/observers/:observer", updateObserver)
config.DELETE("/observers/:observer", deleteObserver)
config.GET("/recorders", getRecorderList)
config.GET("/recorders/:recorder", getRecorder)
config.POST("/recorders", createRecorder)
config.PUT("/recorders/:recorder", updateRecorder)
config.DELETE("/recorders/:recorder", deleteRecorder)
config.GET("/sds", getSDList)
config.GET("/sds/:sd", getSD)
config.POST("/sds", createSD)
config.PUT("/sds/:sd", updateSD)
config.DELETE("/sds/:sd", deleteSD)
config.GET("/limiters", getLimiterList)
config.GET("/limiters/:limiter", getLimiter)
config.POST("/limiters", createLimiter)
config.PUT("/limiters/:limiter", updateLimiter)
config.DELETE("/limiters/:limiter", deleteLimiter)
config.GET("/climiters", getConnLimiterList)
config.GET("/climiters/:limiter", getConnLimiter)
config.POST("/climiters", createConnLimiter)
config.PUT("/climiters/:limiter", updateConnLimiter)
config.DELETE("/climiters/:limiter", deleteConnLimiter)
config.GET("/rlimiters", getRateLimiterList)
config.GET("/rlimiters/:limiter", getRateLimiter)
config.POST("/rlimiters", createRateLimiter)
config.PUT("/rlimiters/:limiter", updateRateLimiter)
config.DELETE("/rlimiters/:limiter", deleteRateLimiter)
+87
View File
@@ -11,6 +11,93 @@ import (
"github.com/go-gost/x/registry"
)
// swagger:parameters getAdmissionListRequest
type getAdmissionListRequest struct {
}
// successful operation.
// swagger:response getAdmissionListResponse
type getAdmissionListResponse struct {
// in: body
Data admissionList
}
type admissionList struct {
Count int `json:"count"`
List []*config.AdmissionConfig `json:"list"`
}
func getAdmissionList(ctx *gin.Context) {
// swagger:route GET /config/admissions Admission getAdmissionListRequest
//
// Get admission list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getAdmissionListResponse
var req getAdmissionListRequest
ctx.ShouldBindQuery(&req)
list := config.Global().Admissions
var resp getAdmissionListResponse
resp.Data = admissionList{
Count: len(list),
List: list,
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters getAdmissionRequest
type getAdmissionRequest struct {
// in: path
// required: true
Admission string `uri:"admission" json:"admission"`
}
// successful operation.
// swagger:response getAdmissionResponse
type getAdmissionResponse struct {
// in: body
Data *config.AdmissionConfig
}
func getAdmission(ctx *gin.Context) {
// swagger:route GET /config/admissions/{admission} Admission getAdmissionRequest
//
// Get admission.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getAdmissionResponse
var req getAdmissionRequest
ctx.ShouldBindUri(&req)
var resp getAdmissionResponse
for _, admission := range config.Global().Admissions {
if admission == nil {
continue
}
if admission.Name == req.Admission {
resp.Data = admission
}
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters createAdmissionRequest
type createAdmissionRequest struct {
// in: body
+87
View File
@@ -11,6 +11,93 @@ import (
"github.com/go-gost/x/registry"
)
// swagger:parameters getAutherListRequest
type getAutherListRequest struct {
}
// successful operation.
// swagger:response getAutherListResponse
type getAutherListResponse struct {
// in: body
Data autherList
}
type autherList struct {
Count int `json:"count"`
List []*config.AutherConfig `json:"list"`
}
func getAutherList(ctx *gin.Context) {
// swagger:route GET /config/authers Auther getAutherListRequest
//
// Get auther list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getAutherListResponse
var req getAutherListRequest
ctx.ShouldBindQuery(&req)
list := config.Global().Authers
var resp getAutherListResponse
resp.Data = autherList{
Count: len(list),
List: list,
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters getAutherRequest
type getAutherRequest struct {
// in: path
// required: true
Auther string `uri:"auther" json:"auther"`
}
// successful operation.
// swagger:response getAutherResponse
type getAutherResponse struct {
// in: body
Data *config.AutherConfig
}
func getAuther(ctx *gin.Context) {
// swagger:route GET /config/authers/{auther} Auther getAutherRequest
//
// Get auther.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getAutherResponse
var req getAutherRequest
ctx.ShouldBindUri(&req)
var resp getAutherResponse
for _, auther := range config.Global().Authers {
if auther == nil {
continue
}
if auther.Name == req.Auther {
resp.Data = auther
}
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters createAutherRequest
type createAutherRequest struct {
// in: body
+87
View File
@@ -11,6 +11,93 @@ import (
"github.com/go-gost/x/registry"
)
// swagger:parameters getBypassListRequest
type getBypassListRequest struct {
}
// successful operation.
// swagger:response getBypassListResponse
type getBypassListResponse struct {
// in: body
Data bypassList
}
type bypassList struct {
Count int `json:"count"`
List []*config.BypassConfig `json:"list"`
}
func getBypassList(ctx *gin.Context) {
// swagger:route GET /config/bypasses Bypass getBypassListRequest
//
// Get bypass list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getBypassListResponse
var req getBypassListRequest
ctx.ShouldBindQuery(&req)
list := config.Global().Bypasses
var resp getBypassListResponse
resp.Data = bypassList{
Count: len(list),
List: list,
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters getBypassRequest
type getBypassRequest struct {
// in: path
// required: true
Bypass string `uri:"bypass" json:"bypass"`
}
// successful operation.
// swagger:response getBypassResponse
type getBypassResponse struct {
// in: body
Data *config.BypassConfig
}
func getBypass(ctx *gin.Context) {
// swagger:route GET /config/bypasses/{bypass} Bypass getBypassRequest
//
// Get bypass.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getBypassResponse
var req getBypassRequest
ctx.ShouldBindUri(&req)
var resp getBypassResponse
for _, bypass := range config.Global().Bypasses {
if bypass == nil {
continue
}
if bypass.Name == req.Bypass {
resp.Data = bypass
}
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters createBypassRequest
type createBypassRequest struct {
// in: body
+87
View File
@@ -12,6 +12,93 @@ import (
"github.com/go-gost/x/registry"
)
// swagger:parameters getChainListRequest
type getChainListRequest struct {
}
// successful operation.
// swagger:response getChainListResponse
type getChainListResponse struct {
// in: body
Data chainList
}
type chainList struct {
Count int `json:"count"`
List []*config.ChainConfig `json:"list"`
}
func getChainList(ctx *gin.Context) {
// swagger:route GET /config/chains Chain getChainListRequest
//
// Get chain list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getChainListResponse
var req getChainListRequest
ctx.ShouldBindQuery(&req)
list := config.Global().Chains
var resp getChainListResponse
resp.Data = chainList{
Count: len(list),
List: list,
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters getChainRequest
type getChainRequest struct {
// in: path
// required: true
Chain string `uri:"chain" json:"chain"`
}
// successful operation.
// swagger:response getChainResponse
type getChainResponse struct {
// in: body
Data *config.ChainConfig
}
func getChain(ctx *gin.Context) {
// swagger:route GET /config/chains/{chain} Chain getChainRequest
//
// Get chain.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getChainResponse
var req getChainRequest
ctx.ShouldBindUri(&req)
var resp getChainResponse
for _, chain := range config.Global().Chains {
if chain == nil {
continue
}
if chain.Name == req.Chain {
resp.Data = chain
}
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters createChainRequest
type createChainRequest struct {
// in: body
+87
View File
@@ -11,6 +11,93 @@ import (
"github.com/go-gost/x/registry"
)
// swagger:parameters getConnLimiterListRequest
type getConnLimiterListRequest struct {
}
// successful operation.
// swagger:response getConnLimiterListResponse
type getConnLimiterListResponse struct {
// in: body
Data connLimiterList
}
type connLimiterList struct {
Count int `json:"count"`
List []*config.LimiterConfig `json:"list"`
}
func getConnLimiterList(ctx *gin.Context) {
// swagger:route GET /config/climiters Limiter getConnLimiterListRequest
//
// Get conn limiter list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getConnLimiterListResponse
var req getConnLimiterListRequest
ctx.ShouldBindQuery(&req)
list := config.Global().CLimiters
var resp getConnLimiterListResponse
resp.Data = connLimiterList{
Count: len(list),
List: list,
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters getConnLimiterRequest
type getConnLimiterRequest struct {
// in: path
// required: true
Limiter string `uri:"limiter" json:"limiter"`
}
// successful operation.
// swagger:response getConnLimiterResponse
type getConnLimiterResponse struct {
// in: body
Data *config.LimiterConfig
}
func getConnLimiter(ctx *gin.Context) {
// swagger:route GET /config/climiters/{limiter} Limiter getConnLimiterRequest
//
// Get conn limiter.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getConnLimiterResponse
var req getConnLimiterRequest
ctx.ShouldBindUri(&req)
var resp getConnLimiterResponse
for _, limiter := range config.Global().CLimiters{
if limiter == nil {
continue
}
if limiter.Name == req.Limiter {
resp.Data = limiter
}
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters createConnLimiterRequest
type createConnLimiterRequest struct {
// in: body
+87
View File
@@ -12,6 +12,93 @@ import (
"github.com/go-gost/x/registry"
)
// swagger:parameters getHopListRequest
type getHopListRequest struct {
}
// successful operation.
// swagger:response getHopListResponse
type getHopListResponse struct {
// in: body
Data hopList
}
type hopList struct {
Count int `json:"count"`
List []*config.HopConfig `json:"list"`
}
func getHopList(ctx *gin.Context) {
// swagger:route GET /config/hops Hop getHopListRequest
//
// Get hop list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getHopListResponse
var req getHopListRequest
ctx.ShouldBindQuery(&req)
list := config.Global().Hops
var resp getHopListResponse
resp.Data = hopList{
Count: len(list),
List: list,
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters getHopRequest
type getHopRequest struct {
// in: path
// required: true
Hop string `uri:"hop" json:"hop"`
}
// successful operation.
// swagger:response getHopResponse
type getHopResponse struct {
// in: body
Data *config.HopConfig
}
func getHop(ctx *gin.Context) {
// swagger:route GET /config/hops/{hop} Hop getHopRequest
//
// Get hop.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getHopResponse
var req getHopRequest
ctx.ShouldBindUri(&req)
var resp getHopResponse
for _, hop := range config.Global().Hops {
if hop == nil {
continue
}
if hop.Name == req.Hop {
resp.Data = hop
}
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters createHopRequest
type createHopRequest struct {
// in: body
+87
View File
@@ -11,6 +11,93 @@ import (
"github.com/go-gost/x/registry"
)
// swagger:parameters getHostsListRequest
type getHostsListRequest struct {
}
// successful operation.
// swagger:response getHostsListResponse
type getHostsListResponse struct {
// in: body
Data hostsList
}
type hostsList struct {
Count int `json:"count"`
List []*config.HostsConfig `json:"list"`
}
func getHostsList(ctx *gin.Context) {
// swagger:route GET /config/hosts Hosts getHostsListRequest
//
// Get hosts list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getHostsListResponse
var req getHostsListRequest
ctx.ShouldBindQuery(&req)
list := config.Global().Hosts
var resp getHostsListResponse
resp.Data = hostsList{
Count: len(list),
List: list,
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters getHostsRequest
type getHostsRequest struct {
// in: path
// required: true
Hosts string `uri:"hosts" json:"hosts"`
}
// successful operation.
// swagger:response getHostsResponse
type getHostsResponse struct {
// in: body
Data *config.HostsConfig
}
func getHosts(ctx *gin.Context) {
// swagger:route GET /config/hosts/{hosts} Hosts getHostsRequest
//
// Get hosts.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getHostsResponse
var req getHostsRequest
ctx.ShouldBindUri(&req)
var resp getHostsResponse
for _, hosts := range config.Global().Hosts {
if hosts == nil {
continue
}
if hosts.Name == req.Hosts {
resp.Data = hosts
}
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters createHostsRequest
type createHostsRequest struct {
// in: body
+87
View File
@@ -11,6 +11,93 @@ import (
"github.com/go-gost/x/registry"
)
// swagger:parameters getIngressListRequest
type getIngressListRequest struct {
}
// successful operation.
// swagger:response getIngressListResponse
type getIngressListResponse struct {
// in: body
Data ingressList
}
type ingressList struct {
Count int `json:"count"`
List []*config.IngressConfig `json:"list"`
}
func getIngressList(ctx *gin.Context) {
// swagger:route GET /config/ingresses Ingress getIngressListRequest
//
// Get ingress list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getIngressListResponse
var req getIngressListRequest
ctx.ShouldBindQuery(&req)
list := config.Global().Ingresses
var resp getIngressListResponse
resp.Data = ingressList{
Count: len(list),
List: list,
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters getIngressRequest
type getIngressRequest struct {
// in: path
// required: true
Ingress string `uri:"ingress" json:"ingress"`
}
// successful operation.
// swagger:response getIngressResponse
type getIngressResponse struct {
// in: body
Data *config.IngressConfig
}
func getIngress(ctx *gin.Context) {
// swagger:route GET /config/ingresses/{ingress} Ingress getIngressRequest
//
// Get ingress.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getIngressResponse
var req getIngressRequest
ctx.ShouldBindUri(&req)
var resp getIngressResponse
for _, ingress := range config.Global().Ingresses {
if ingress == nil {
continue
}
if ingress.Name == req.Ingress {
resp.Data = ingress
}
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters createIngressRequest
type createIngressRequest struct {
// in: body
+87
View File
@@ -11,6 +11,93 @@ import (
"github.com/go-gost/x/registry"
)
// swagger:parameters getLimiterListRequest
type getLimiterListRequest struct {
}
// successful operation.
// swagger:response getLimiterListResponse
type getLimiterListResponse struct {
// in: body
Data limiterList
}
type limiterList struct {
Count int `json:"count"`
List []*config.LimiterConfig `json:"list"`
}
func getLimiterList(ctx *gin.Context) {
// swagger:route GET /config/limiters Limiter getLimiterListRequest
//
// Get limiter list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getLimiterListResponse
var req getLimiterListRequest
ctx.ShouldBindQuery(&req)
list := config.Global().Limiters
var resp getLimiterListResponse
resp.Data = limiterList{
Count: len(list),
List: list,
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters getLimiterRequest
type getLimiterRequest struct {
// in: path
// required: true
Limiter string `uri:"limiter" json:"limiter"`
}
// successful operation.
// swagger:response getLimiterResponse
type getLimiterResponse struct {
// in: body
Data *config.LimiterConfig
}
func getLimiter(ctx *gin.Context) {
// swagger:route GET /config/limiters/{limiter} Limiter getLimiterRequest
//
// Get limiter.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getLimiterResponse
var req getLimiterRequest
ctx.ShouldBindUri(&req)
var resp getLimiterResponse
for _, limiter := range config.Global().Limiters {
if limiter == nil {
continue
}
if limiter.Name == req.Limiter {
resp.Data = limiter
}
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters createLimiterRequest
type createLimiterRequest struct {
// in: body
+87
View File
@@ -11,6 +11,93 @@ import (
"github.com/go-gost/x/registry"
)
// swagger:parameters getObserverListRequest
type getObserverListRequest struct {
}
// successful operation.
// swagger:response getObserverListResponse
type getObserverListResponse struct {
// in: body
Data observerList
}
type observerList struct {
Count int `json:"count"`
List []*config.ObserverConfig `json:"list"`
}
func getObserverList(ctx *gin.Context) {
// swagger:route GET /config/observers Observer getObserverListRequest
//
// Get observer list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getObserverListResponse
var req getObserverListRequest
ctx.ShouldBindQuery(&req)
list := config.Global().Observers
var resp getObserverListResponse
resp.Data = observerList{
Count: len(list),
List: list,
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters getObserverRequest
type getObserverRequest struct {
// in: path
// required: true
Observer string `uri:"observer" json:"observer"`
}
// successful operation.
// swagger:response getObserverResponse
type getObserverResponse struct {
// in: body
Data *config.ObserverConfig
}
func getObserver(ctx *gin.Context) {
// swagger:route GET /config/observers/{observer} Observer getObserverRequest
//
// Get observer.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getObserverResponse
var req getObserverRequest
ctx.ShouldBindUri(&req)
var resp getObserverResponse
for _, observer := range config.Global().Observers {
if observer == nil {
continue
}
if observer.Name == req.Observer {
resp.Data = observer
}
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters createObserverRequest
type createObserverRequest struct {
// in: body
+87
View File
@@ -11,6 +11,93 @@ import (
"github.com/go-gost/x/registry"
)
// swagger:parameters getRateLimiterListRequest
type getRateLimiterListRequest struct {
}
// successful operation.
// swagger:response getRateLimiterListResponse
type getRateLimiterListResponse struct {
// in: body
Data rateLimiterList
}
type rateLimiterList struct {
Count int `json:"count"`
List []*config.LimiterConfig `json:"list"`
}
func getRateLimiterList(ctx *gin.Context) {
// swagger:route GET /config/rlimiters Limiter getRateLimiterListRequest
//
// Get rate limiter list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getRateLimiterListResponse
var req getRateLimiterListRequest
ctx.ShouldBindQuery(&req)
list := config.Global().CLimiters
var resp getRateLimiterListResponse
resp.Data = rateLimiterList{
Count: len(list),
List: list,
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters getRateLimiterRequest
type getRateLimiterRequest struct {
// in: path
// required: true
Limiter string `uri:"limiter" json:"limiter"`
}
// successful operation.
// swagger:response getRateLimiterResponse
type getRateLimiterResponse struct {
// in: body
Data *config.LimiterConfig
}
func getRateLimiter(ctx *gin.Context) {
// swagger:route GET /config/rlimiters/{limiter} Limiter getRateLimiterRequest
//
// Get conn limiter.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getRateLimiterResponse
var req getRateLimiterRequest
ctx.ShouldBindUri(&req)
var resp getRateLimiterResponse
for _, limiter := range config.Global().CLimiters{
if limiter == nil {
continue
}
if limiter.Name == req.Limiter {
resp.Data = limiter
}
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters createRateLimiterRequest
type createRateLimiterRequest struct {
// in: body
+87
View File
@@ -11,6 +11,93 @@ import (
"github.com/go-gost/x/registry"
)
// swagger:parameters getRecorderListRequest
type getRecorderListRequest struct {
}
// successful operation.
// swagger:response getRecorderListResponse
type getRecorderListResponse struct {
// in: body
Data recorderList
}
type recorderList struct {
Count int `json:"count"`
List []*config.RecorderConfig `json:"list"`
}
func getRecorderList(ctx *gin.Context) {
// swagger:route GET /config/recorders Recorder getRecorderListRequest
//
// Get recorder list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getRecorderListResponse
var req getRecorderListRequest
ctx.ShouldBindQuery(&req)
list := config.Global().Recorders
var resp getRecorderListResponse
resp.Data = recorderList{
Count: len(list),
List: list,
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters getRecorderRequest
type getRecorderRequest struct {
// in: path
// required: true
Recorder string `uri:"recorder" json:"recorder"`
}
// successful operation.
// swagger:response getRecorderResponse
type getRecorderResponse struct {
// in: body
Data *config.RecorderConfig
}
func getRecorder(ctx *gin.Context) {
// swagger:route GET /config/recorders/{recorder} Recorder getRecorderRequest
//
// Get recorder.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getRecorderResponse
var req getRecorderRequest
ctx.ShouldBindUri(&req)
var resp getRecorderResponse
for _, recorder := range config.Global().Recorders {
if recorder == nil {
continue
}
if recorder.Name == req.Recorder {
resp.Data = recorder
}
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters createRecorderRequest
type createRecorderRequest struct {
// in: body
+56
View File
@@ -0,0 +1,56 @@
package api
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/go-gost/x/config"
"github.com/go-gost/x/config/loader"
"github.com/go-gost/x/config/parsing/parser"
"github.com/go-gost/x/registry"
)
// swagger:parameters reloadConfigRequest
type reloadConfigRequest struct{}
// successful operation.
// swagger:response reloadConfigResponse
type reloadConfigResponse struct {
Data Response
}
func reloadConfig(ctx *gin.Context) {
// swagger:route POST /config/reload Reload reloadConfigRequest
//
// Hot reload config.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: reloadConfigResponse
cfg, err := parser.Parse()
if err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, err.Error()))
return
}
config.Set(cfg)
if err := loader.Load(cfg); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, err.Error()))
return
}
for _, svc := range registry.ServiceRegistry().GetAll() {
svc := svc
go func() {
svc.Serve()
}()
}
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
+87
View File
@@ -11,6 +11,93 @@ import (
"github.com/go-gost/x/registry"
)
// swagger:parameters getResolverListRequest
type getResolverListRequest struct {
}
// successful operation.
// swagger:response getResolverListResponse
type getResolverListResponse struct {
// in: body
Data resolverList
}
type resolverList struct {
Count int `json:"count"`
List []*config.ResolverConfig `json:"list"`
}
func getResolverList(ctx *gin.Context) {
// swagger:route GET /config/resolvers Resolver getResolverListRequest
//
// Get resolver list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getResolverListResponse
var req getResolverListRequest
ctx.ShouldBindQuery(&req)
list := config.Global().Resolvers
var resp getResolverListResponse
resp.Data = resolverList{
Count: len(list),
List: list,
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters getResolverRequest
type getResolverRequest struct {
// in: path
// required: true
Resolver string `uri:"resolver" json:"resolver"`
}
// successful operation.
// swagger:response getResolverResponse
type getResolverResponse struct {
// in: body
Data *config.ResolverConfig
}
func getResolver(ctx *gin.Context) {
// swagger:route GET /config/resolvers/{resolver} Resolver getResolverRequest
//
// Get resolver.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getResolverResponse
var req getResolverRequest
ctx.ShouldBindUri(&req)
var resp getResolverResponse
for _, resolver := range config.Global().Resolvers {
if resolver == nil {
continue
}
if resolver.Name == req.Resolver {
resp.Data = resolver
}
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters createResolverRequest
type createResolverRequest struct {
// in: body
+87
View File
@@ -11,6 +11,93 @@ import (
"github.com/go-gost/x/registry"
)
// swagger:parameters getRouterListRequest
type getRouterListRequest struct {
}
// successful operation.
// swagger:response getRouterListResponse
type getRouterListResponse struct {
// in: body
Data routerList
}
type routerList struct {
Count int `json:"count"`
List []*config.RouterConfig `json:"list"`
}
func getRouterList(ctx *gin.Context) {
// swagger:route GET /config/routers Router getRouterListRequest
//
// Get router list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getRouterListResponse
var req getRouterListRequest
ctx.ShouldBindQuery(&req)
list := config.Global().Routers
var resp getRouterListResponse
resp.Data = routerList{
Count: len(list),
List: list,
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters getRouterRequest
type getRouterRequest struct {
// in: path
// required: true
Router string `uri:"router" json:"router"`
}
// successful operation.
// swagger:response getRouterResponse
type getRouterResponse struct {
// in: body
Data *config.RouterConfig
}
func getRouter(ctx *gin.Context) {
// swagger:route GET /config/routers/{router} Router getRouterRequest
//
// Get router.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getRouterResponse
var req getRouterRequest
ctx.ShouldBindUri(&req)
var resp getRouterResponse
for _, router := range config.Global().Routers {
if router == nil {
continue
}
if router.Name == req.Router {
resp.Data = router
}
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters createRouterRequest
type createRouterRequest struct {
// in: body
+87
View File
@@ -11,6 +11,93 @@ import (
"github.com/go-gost/x/registry"
)
// swagger:parameters getSDListRequest
type getSDListRequest struct {
}
// successful operation.
// swagger:response getSDListResponse
type getSDListResponse struct {
// in: body
Data sdList
}
type sdList struct {
Count int `json:"count"`
List []*config.SDConfig `json:"list"`
}
func getSDList(ctx *gin.Context) {
// swagger:route GET /config/sds SD getSDListRequest
//
// Get sd list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getSDListResponse
var req getSDListRequest
ctx.ShouldBindQuery(&req)
list := config.Global().SDs
var resp getSDListResponse
resp.Data = sdList{
Count: len(list),
List: list,
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters getSDRequest
type getSDRequest struct {
// in: path
// required: true
SD string `uri:"sd" json:"sd"`
}
// successful operation.
// swagger:response getSDResponse
type getSDResponse struct {
// in: body
Data *config.SDConfig
}
func getSD(ctx *gin.Context) {
// swagger:route GET /config/sds/{sd} SD getSDRequest
//
// Get sd.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getSDResponse
var req getSDRequest
ctx.ShouldBindUri(&req)
var resp getSDResponse
for _, sd := range config.Global().SDs {
if sd == nil {
continue
}
if sd.Name == req.SD {
resp.Data = sd
}
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters createSDRequest
type createSDRequest struct {
// in: body
+87
View File
@@ -11,6 +11,93 @@ import (
"github.com/go-gost/x/registry"
)
// swagger:parameters getServiceListRequest
type getServiceListRequest struct {
}
// successful operation.
// swagger:response getServiceListResponse
type getServiceListResponse struct {
// in: body
Data serviceList
}
type serviceList struct {
Count int `json:"count"`
List []*config.ServiceConfig `json:"list"`
}
func getServiceList(ctx *gin.Context) {
// swagger:route GET /config/services Service getServiceListRequest
//
// Get service list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getServiceListResponse
var req getServiceListRequest
ctx.ShouldBindQuery(&req)
list := config.Global().Services
var resp getServiceListResponse
resp.Data = serviceList{
Count: len(list),
List: list,
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters getServiceRequest
type getServiceRequest struct {
// in: path
// required: true
Service string `uri:"service" json:"service"`
}
// successful operation.
// swagger:response getServiceResponse
type getServiceResponse struct {
// in: body
Data *config.ServiceConfig
}
func getService(ctx *gin.Context) {
// swagger:route GET /config/services/{service} Service getServiceRequest
//
// Get service.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getServiceResponse
var req getServiceRequest
ctx.ShouldBindUri(&req)
var resp getServiceResponse
for _, service := range config.Global().Services {
if service == nil {
continue
}
if service.Name == req.Service {
resp.Data = service
}
}
ctx.JSON(http.StatusOK, Response{
Data: resp.Data,
})
}
// swagger:parameters createServiceRequest
type createServiceRequest struct {
// in: body
+1 -1
View File
@@ -35,7 +35,7 @@ func mwBasicAuth(auther auth.Authenticator) gin.HandlerFunc {
return
}
u, p, _ := c.Request.BasicAuth()
if _, ok := auther.Authenticate(c, u, p); !ok {
if _, ok := auther.Authenticate(c, u, p, auth.WithService("@api")); !ok {
c.Writer.Header().Set("WWW-Authenticate", "Basic")
c.JSON(http.StatusUnauthorized, Response{
Code: http.StatusUnauthorized,
+96
View File
@@ -0,0 +1,96 @@
package service
import (
"net"
"net/http"
"github.com/gin-gonic/gin"
"github.com/go-gost/core/auth"
"github.com/go-gost/core/service"
"github.com/go-gost/x/api"
)
type options struct {
accessLog bool
pathPrefix string
auther auth.Authenticator
}
type Option func(*options)
func PathPrefixOption(pathPrefix string) Option {
return func(o *options) {
o.pathPrefix = pathPrefix
}
}
func AccessLogOption(enable bool) Option {
return func(o *options) {
o.accessLog = enable
}
}
func AutherOption(auther auth.Authenticator) Option {
return func(o *options) {
o.auther = auther
}
}
type server struct {
s *http.Server
ln net.Listener
cclose chan struct{}
}
func NewService(network, addr string, opts ...Option) (service.Service, error) {
if network == "" {
network = "tcp"
}
ln, err := net.Listen(network, addr)
if err != nil {
return nil, err
}
var options options
for _, opt := range opts {
opt(&options)
}
gin.SetMode(gin.ReleaseMode)
r := gin.New()
api.Register(r, &api.Options{
AccessLog: options.accessLog,
PathPrefix: options.pathPrefix,
Auther: options.auther,
})
return &server{
s: &http.Server{
Handler: r,
},
ln: ln,
cclose: make(chan struct{}),
}, nil
}
func (s *server) Serve() error {
return s.s.Serve(s.ln)
}
func (s *server) Addr() net.Addr {
return s.ln.Addr()
}
func (s *server) Close() error {
return s.s.Close()
}
func (s *server) IsClosed() bool {
select {
case <-s.cclose:
return true
default:
return false
}
}
+898 -8
View File
File diff suppressed because it is too large Load Diff
+47 -17
View File
@@ -67,6 +67,7 @@ type authenticator struct {
mu sync.RWMutex
cancelFunc context.CancelFunc
options options
logger logger.Logger
}
// NewAuthenticator creates an Authenticator that authenticates client by pre-defined user mapping.
@@ -75,23 +76,19 @@ func NewAuthenticator(opts ...Option) auth.Authenticator {
for _, opt := range opts {
opt(&options)
}
if options.logger == nil {
options.logger = xlogger.Nop()
}
ctx, cancel := context.WithCancel(context.TODO())
ctx, cancel := context.WithCancel(context.Background())
p := &authenticator{
kvs: make(map[string]string),
cancelFunc: cancel,
options: options,
logger: options.logger,
}
if p.logger == nil {
p.logger = xlogger.Nop()
}
if err := p.reload(ctx); err != nil {
options.logger.Warnf("reload: %v", err)
}
if p.options.period > 0 {
go p.periodReload(ctx)
}
go p.periodReload(ctx)
return p
}
@@ -114,7 +111,14 @@ func (p *authenticator) Authenticate(ctx context.Context, user, password string,
}
func (p *authenticator) periodReload(ctx context.Context) error {
if err := p.reload(ctx); err != nil {
p.logger.Warnf("reload: %v", err)
}
period := p.options.period
if period <= 0 {
return nil
}
if period < time.Second {
period = time.Second
}
@@ -125,7 +129,7 @@ func (p *authenticator) periodReload(ctx context.Context) error {
select {
case <-ticker.C:
if err := p.reload(ctx); err != nil {
p.options.logger.Warnf("reload: %v", err)
p.logger.Warnf("reload: %v", err)
// return err
}
case <-ctx.Done():
@@ -145,7 +149,7 @@ func (p *authenticator) reload(ctx context.Context) (err error) {
kvs[k] = v
}
p.options.logger.Debugf("load items %d", len(m))
p.logger.Debugf("load items %d", len(m))
p.mu.Lock()
defer p.mu.Unlock()
@@ -162,13 +166,13 @@ func (p *authenticator) load(ctx context.Context) (m map[string]string, err erro
if mapper, ok := p.options.fileLoader.(loader.Mapper); ok {
auths, er := mapper.Map(ctx)
if er != nil {
p.options.logger.Warnf("file loader: %v", er)
p.logger.Warnf("file loader: %v", er)
}
m = auths
} else {
r, er := p.options.fileLoader.Load(ctx)
if er != nil {
p.options.logger.Warnf("file loader: %v", er)
p.logger.Warnf("file loader: %v", er)
}
if auths, _ := p.parseAuths(r); auths != nil {
m = auths
@@ -179,7 +183,7 @@ func (p *authenticator) load(ctx context.Context) (m map[string]string, err erro
if mapper, ok := p.options.fileLoader.(loader.Mapper); ok {
auths, er := mapper.Map(ctx)
if er != nil {
p.options.logger.Warnf("file loader: %v", er)
p.logger.Warnf("file loader: %v", er)
}
for k, v := range auths {
m[k] = v
@@ -187,7 +191,7 @@ func (p *authenticator) load(ctx context.Context) (m map[string]string, err erro
} else {
r, er := p.options.redisLoader.Load(ctx)
if er != nil {
p.options.logger.Warnf("redis loader: %v", er)
p.logger.Warnf("redis loader: %v", er)
}
if auths, _ := p.parseAuths(r); auths != nil {
for k, v := range auths {
@@ -199,7 +203,7 @@ func (p *authenticator) load(ctx context.Context) (m map[string]string, err erro
if p.options.httpLoader != nil {
r, er := p.options.httpLoader.Load(ctx)
if er != nil {
p.options.logger.Warnf("http loader: %v", er)
p.logger.Warnf("http loader: %v", er)
}
if auths, _ := p.parseAuths(r); auths != nil {
for k, v := range auths {
@@ -252,3 +256,29 @@ func (p *authenticator) Close() error {
}
return nil
}
type authenticatorGroup struct {
authers []auth.Authenticator
}
func AuthenticatorGroup(authers ...auth.Authenticator) auth.Authenticator {
return &authenticatorGroup{
authers: authers,
}
}
func (p *authenticatorGroup) Authenticate(ctx context.Context, user, password string, opts ...auth.Option) (string, bool) {
if len(p.authers) == 0 {
return "", false
}
for _, auther := range p.authers {
if auther == nil {
continue
}
if id, ok := auther.Authenticate(ctx, user, password, opts...); ok {
return id, ok
}
}
return "", false
}
+12 -2
View File
@@ -7,7 +7,7 @@ import (
"github.com/go-gost/core/auth"
"github.com/go-gost/core/logger"
"github.com/go-gost/plugin/auth/proto"
ctxvalue "github.com/go-gost/x/ctx"
xctx "github.com/go-gost/x/ctx"
"github.com/go-gost/x/internal/plugin"
"google.golang.org/grpc"
)
@@ -51,11 +51,21 @@ func (p *grpcPlugin) Authenticate(ctx context.Context, user, password string, op
return "", false
}
var options auth.Options
for _, opt := range opts {
opt(&options)
}
var clientAddr string
if v := xctx.SrcAddrFromContext(ctx); v != nil {
clientAddr = v.String()
}
r, err := p.client.Authenticate(ctx,
&proto.AuthenticateRequest{
Service: options.Service,
Username: user,
Password: password,
Client: string(ctxvalue.ClientAddrFromContext(ctx)),
Client: clientAddr,
})
if err != nil {
p.log.Error(err)
+14 -2
View File
@@ -8,11 +8,12 @@ import (
"github.com/go-gost/core/auth"
"github.com/go-gost/core/logger"
ctxvalue "github.com/go-gost/x/ctx"
xctx "github.com/go-gost/x/ctx"
"github.com/go-gost/x/internal/plugin"
)
type httpPluginRequest struct {
Service string `json:"service"`
Username string `json:"username"`
Password string `json:"password"`
Client string `json:"client"`
@@ -53,10 +54,21 @@ func (p *httpPlugin) Authenticate(ctx context.Context, user, password string, op
return
}
var options auth.Options
for _, opt := range opts {
opt(&options)
}
var clientAddr string
if v := xctx.SrcAddrFromContext(ctx); v != nil {
clientAddr = v.String()
}
rb := httpPluginRequest{
Service: options.Service,
Username: user,
Password: password,
Client: string(ctxvalue.ClientAddrFromContext(ctx)),
Client: clientAddr,
}
v, err := json.Marshal(&rb)
if err != nil {
+147 -66
View File
@@ -6,14 +6,19 @@ import (
"errors"
"io"
"net"
"slices"
"strings"
"sync"
"time"
"github.com/go-gost/core/bypass"
"github.com/go-gost/core/logger"
ctxvalue "github.com/go-gost/x/ctx"
"github.com/go-gost/x/internal/loader"
"github.com/go-gost/x/internal/matcher"
xnet "github.com/go-gost/x/internal/net"
xlogger "github.com/go-gost/x/logger"
"github.com/gobwas/glob"
)
var (
@@ -78,9 +83,11 @@ type localBypass struct {
cidrMatcher matcher.Matcher
addrMatcher matcher.Matcher
wildcardMatcher matcher.Matcher
cancelFunc context.CancelFunc
ipRangeMatcher matcher.Matcher
options options
logger logger.Logger
mu sync.RWMutex
cancelFunc context.CancelFunc
}
// NewBypass creates and initializes a new Bypass.
@@ -91,36 +98,47 @@ func NewBypass(opts ...Option) bypass.Bypass {
opt(&options)
}
ctx, cancel := context.WithCancel(context.TODO())
ctx, cancel := context.WithCancel(context.Background())
bp := &localBypass{
cancelFunc: cancel,
options: options,
p := &localBypass{
cidrMatcher: matcher.NopMatcher(),
addrMatcher: matcher.NopMatcher(),
wildcardMatcher: matcher.NopMatcher(),
ipRangeMatcher: matcher.NopMatcher(),
cancelFunc: cancel,
options: options,
logger: options.logger,
}
if p.logger == nil {
p.logger = xlogger.Nop()
}
if err := bp.reload(ctx); err != nil {
options.logger.Warnf("reload: %v", err)
}
if bp.options.period > 0 {
go bp.periodReload(ctx)
}
go p.periodReload(ctx)
return bp
return p
}
func (bp *localBypass) periodReload(ctx context.Context) error {
period := bp.options.period
func (p *localBypass) periodReload(ctx context.Context) error {
if err := p.reload(ctx); err != nil {
p.logger.Warnf("reload: %v", err)
}
period := p.options.period
if period <= 0 {
return nil
}
if period < time.Second {
period = time.Second
}
ticker := time.NewTicker(period)
defer ticker.Stop()
for {
select {
case <-ticker.C:
if err := bp.reload(ctx); err != nil {
bp.options.logger.Warnf("reload: %v", err)
if err := p.reload(ctx); err != nil {
p.logger.Warnf("reload: %v", err)
// return err
}
case <-ctx.Done():
@@ -129,84 +147,96 @@ func (bp *localBypass) periodReload(ctx context.Context) error {
}
}
func (bp *localBypass) reload(ctx context.Context) error {
v, err := bp.load(ctx)
func (p *localBypass) reload(ctx context.Context) error {
v, err := p.load(ctx)
if err != nil {
return err
}
patterns := append(bp.options.matchers, v...)
bp.options.logger.Debugf("load items %d", len(patterns))
patterns := append(p.options.matchers, v...)
p.logger.Debugf("load items %d", len(patterns))
var addrs []string
var inets []*net.IPNet
var wildcards []string
var ipRanges []xnet.IPRange
for _, pattern := range patterns {
if _, inet, err := net.ParseCIDR(pattern); err == nil {
inets = append(inets, inet)
continue
}
if strings.ContainsAny(pattern, "*?") {
wildcards = append(wildcards, pattern)
if _, err := glob.Compile(pattern); err == nil {
wildcards = append(wildcards, pattern)
continue
}
}
r := xnet.IPRange{}
if err := r.Parse(pattern); err == nil {
ipRanges = append(ipRanges, r)
continue
}
addrs = append(addrs, pattern)
}
bp.mu.Lock()
defer bp.mu.Unlock()
p.mu.Lock()
defer p.mu.Unlock()
bp.cidrMatcher = matcher.CIDRMatcher(inets)
bp.addrMatcher = matcher.AddrMatcher(addrs)
bp.wildcardMatcher = matcher.WildcardMatcher(wildcards)
p.cidrMatcher = matcher.CIDRMatcher(inets)
p.addrMatcher = matcher.AddrMatcher(addrs)
p.wildcardMatcher = matcher.WildcardMatcher(wildcards)
p.ipRangeMatcher = matcher.IPRangeMatcher(ipRanges)
return nil
}
func (bp *localBypass) load(ctx context.Context) (patterns []string, err error) {
if bp.options.fileLoader != nil {
if lister, ok := bp.options.fileLoader.(loader.Lister); ok {
func (p *localBypass) load(ctx context.Context) (patterns []string, err error) {
if p.options.fileLoader != nil {
if lister, ok := p.options.fileLoader.(loader.Lister); ok {
list, er := lister.List(ctx)
if er != nil {
bp.options.logger.Warnf("file loader: %v", er)
p.logger.Warnf("file loader: %v", er)
}
for _, s := range list {
if line := bp.parseLine(s); line != "" {
if line := p.parseLine(s); line != "" {
patterns = append(patterns, line)
}
}
} else {
r, er := bp.options.fileLoader.Load(ctx)
r, er := p.options.fileLoader.Load(ctx)
if er != nil {
bp.options.logger.Warnf("file loader: %v", er)
p.logger.Warnf("file loader: %v", er)
}
if v, _ := bp.parsePatterns(r); v != nil {
if v, _ := p.parsePatterns(r); v != nil {
patterns = append(patterns, v...)
}
}
}
if bp.options.redisLoader != nil {
if lister, ok := bp.options.redisLoader.(loader.Lister); ok {
if p.options.redisLoader != nil {
if lister, ok := p.options.redisLoader.(loader.Lister); ok {
list, er := lister.List(ctx)
if er != nil {
bp.options.logger.Warnf("redis loader: %v", er)
p.logger.Warnf("redis loader: %v", er)
}
patterns = append(patterns, list...)
} else {
r, er := bp.options.redisLoader.Load(ctx)
r, er := p.options.redisLoader.Load(ctx)
if er != nil {
bp.options.logger.Warnf("redis loader: %v", er)
p.logger.Warnf("redis loader: %v", er)
}
if v, _ := bp.parsePatterns(r); v != nil {
if v, _ := p.parsePatterns(r); v != nil {
patterns = append(patterns, v...)
}
}
}
if bp.options.httpLoader != nil {
r, er := bp.options.httpLoader.Load(ctx)
if p.options.httpLoader != nil {
r, er := p.options.httpLoader.Load(ctx)
if er != nil {
bp.options.logger.Warnf("http loader: %v", er)
p.logger.Warnf("http loader: %v", er)
}
if v, _ := bp.parsePatterns(r); v != nil {
if v, _ := p.parsePatterns(r); v != nil {
patterns = append(patterns, v...)
}
}
@@ -214,14 +244,14 @@ func (bp *localBypass) load(ctx context.Context) (patterns []string, err error)
return
}
func (bp *localBypass) parsePatterns(r io.Reader) (patterns []string, err error) {
func (p *localBypass) parsePatterns(r io.Reader) (patterns []string, err error) {
if r == nil {
return
}
scanner := bufio.NewScanner(r)
for scanner.Scan() {
if line := bp.parseLine(scanner.Text()); line != "" {
if line := p.parseLine(scanner.Text()); line != "" {
patterns = append(patterns, line)
}
}
@@ -230,19 +260,24 @@ func (bp *localBypass) parsePatterns(r io.Reader) (patterns []string, err error)
return
}
func (bp *localBypass) Contains(ctx context.Context, network, addr string, opts ...bypass.Option) bool {
if addr == "" || bp == nil {
func (p *localBypass) Contains(ctx context.Context, network, addr string, opts ...bypass.Option) bool {
if addr == "" || p == nil {
return false
}
matched := bp.matched(addr)
matched := p.matched(addr)
b := !p.options.whitelist && matched ||
p.options.whitelist && !matched
log := p.logger.WithFields(map[string]any{
"sid": ctxvalue.SidFromContext(ctx),
})
b := !bp.options.whitelist && matched ||
bp.options.whitelist && !matched
if b {
bp.options.logger.Debugf("bypass: %s, whitelist: %t", addr, bp.options.whitelist)
log.Debugf("bypass: %s, whitelist: %t", addr, p.options.whitelist)
} else {
bp.options.logger.Debugf("pass: %s, whitelist: %t", addr, bp.options.whitelist)
log.Debugf("pass: %s, whitelist: %t", addr, p.options.whitelist)
}
return b
}
@@ -251,18 +286,22 @@ func (p *localBypass) IsWhitelist() bool {
return p.options.whitelist
}
func (bp *localBypass) parseLine(s string) string {
func (p *localBypass) parseLine(s string) string {
if n := strings.IndexByte(s, '#'); n >= 0 {
s = s[:n]
}
return strings.TrimSpace(s)
}
func (bp *localBypass) matched(addr string) bool {
bp.mu.RLock()
defer bp.mu.RUnlock()
func (p *localBypass) matched(addr string) bool {
p.mu.RLock()
defer p.mu.RUnlock()
if bp.addrMatcher.Match(addr) {
if p.ipRangeMatcher.Match(addr) {
return true
}
if p.addrMatcher.Match(addr) {
return true
}
@@ -271,20 +310,62 @@ func (bp *localBypass) matched(addr string) bool {
host = addr
}
if ip := net.ParseIP(host); ip != nil && bp.cidrMatcher.Match(host) {
if ip := net.ParseIP(host); ip != nil && p.cidrMatcher.Match(host) {
return true
}
return bp.wildcardMatcher.Match(addr)
return p.wildcardMatcher.Match(addr)
}
func (bp *localBypass) Close() error {
bp.cancelFunc()
if bp.options.fileLoader != nil {
bp.options.fileLoader.Close()
func (p *localBypass) Close() error {
p.cancelFunc()
if p.options.fileLoader != nil {
p.options.fileLoader.Close()
}
if bp.options.redisLoader != nil {
bp.options.redisLoader.Close()
if p.options.redisLoader != nil {
p.options.redisLoader.Close()
}
return nil
}
type bypassGroup struct {
bypasses []bypass.Bypass
}
func BypassGroup(bypasses ...bypass.Bypass) bypass.Bypass {
return &bypassGroup{
bypasses: bypasses,
}
}
func (p *bypassGroup) Contains(ctx context.Context, network, addr string, opts ...bypass.Option) bool {
var whitelist, blacklist []bool
for _, bypass := range p.bypasses {
result := bypass.Contains(ctx, network, addr, opts...)
if bypass.IsWhitelist() {
whitelist = append(whitelist, result)
} else {
blacklist = append(blacklist, result)
}
}
status := false
if len(whitelist) > 0 {
if slices.Contains(whitelist, false) {
status = false
} else {
status = true
}
}
if !status && len(blacklist) > 0 {
if slices.Contains(blacklist, true) {
status = true
} else {
status = false
}
}
return status
}
func (p *bypassGroup) IsWhitelist() bool {
return false
}
+1
View File
@@ -56,6 +56,7 @@ func (p *grpcPlugin) Contains(ctx context.Context, network, addr string, opts ..
r, err := p.client.Bypass(ctx,
&proto.BypassRequest{
Service: options.Service,
Network: network,
Addr: addr,
Client: string(ctxvalue.ClientIDFromContext(ctx)),
+2
View File
@@ -13,6 +13,7 @@ import (
)
type httpPluginRequest struct {
Service string `json:"service"`
Network string `json:"network"`
Addr string `json:"addr"`
Client string `json:"client"`
@@ -60,6 +61,7 @@ func (p *httpPlugin) Contains(ctx context.Context, network, addr string, opts ..
}
rb := httpPluginRequest{
Service: options.Service,
Network: network,
Addr: addr,
Client: string(ctxvalue.ClientIDFromContext(ctx)),
+1 -1
View File
@@ -38,7 +38,7 @@ func (*defaultRoute) Dial(ctx context.Context, network, address string, opts ...
netd := dialer.Dialer{
Interface: options.Interface,
Netns: options.Netns,
Logger: options.Logger,
Log: options.Logger,
}
if options.SockOpts != nil {
netd.Mark = options.SockOpts.Mark
+5 -4
View File
@@ -10,7 +10,8 @@ import (
"github.com/go-gost/core/chain"
"github.com/go-gost/core/logger"
"github.com/go-gost/core/recorder"
ctxvalue "github.com/go-gost/x/ctx"
xctx "github.com/go-gost/x/ctx"
ictx "github.com/go-gost/x/internal/ctx"
xnet "github.com/go-gost/x/internal/net"
)
@@ -50,7 +51,7 @@ func (r *Router) Dial(ctx context.Context, network, address string) (conn net.Co
r.record(ctx, recorder.RecorderServiceRouterDialAddress, []byte(host))
log := r.options.Logger.WithFields(map[string]any{
"sid": ctxvalue.SidFromContext(ctx),
"sid": xctx.SidFromContext(ctx),
})
conn, err = r.dial(ctx, network, address, log)
@@ -96,7 +97,7 @@ func (r *Router) dial(ctx context.Context, network, address string, log logger.L
defer cancel()
}
buf := ctxvalue.BufferFromContext(ctx)
buf := ictx.BufferFromContext(ctx)
if buf != nil {
buf.Reset()
}
@@ -151,7 +152,7 @@ func (r *Router) Bind(ctx context.Context, network, address string, opts ...chai
}
log := r.options.Logger.WithFields(map[string]any{
"sid": ctxvalue.SidFromContext(ctx),
"sid": xctx.SidFromContext(ctx),
})
log.Debugf("bind on %s/%s", address, network)
+120 -43
View File
@@ -42,24 +42,6 @@ func BuildConfigFromCmd(serviceList, nodeList []string) (*config.Config, error)
return nil, err
}
nodeConfig, err := buildNodeConfig(url)
if err != nil {
return nil, err
}
nodeConfig.Name = fmt.Sprintf("%snode-0", namePrefix)
var nodes []*config.NodeConfig
for _, host := range strings.Split(nodeConfig.Addr, ",") {
if host == "" {
continue
}
nodeCfg := &config.NodeConfig{}
*nodeCfg = *nodeConfig
nodeCfg.Name = fmt.Sprintf("%snode-%d", namePrefix, len(nodes))
nodeCfg.Addr = host
nodes = append(nodes, nodeCfg)
}
m := map[string]any{}
for k, v := range url.Query() {
if len(v) > 0 {
@@ -71,8 +53,6 @@ func BuildConfigFromCmd(serviceList, nodeList []string) (*config.Config, error)
hopConfig := &config.HopConfig{
Name: fmt.Sprintf("%shop-%d", namePrefix, i),
Selector: parseSelector(m),
Nodes: nodes,
Metadata: m,
}
if v := mdutil.GetString(md, "bypass"); v != "" {
@@ -135,15 +115,53 @@ func BuildConfigFromCmd(serviceList, nodeList []string) (*config.Config, error)
}
if v := mdutil.GetString(md, "interface"); v != "" {
hopConfig.Interface = v
m["hop.interface"] = v
delete(m, "interface")
}
if v := mdutil.GetInt(md, "so_mark"); v > 0 {
hopConfig.SockOpts = &config.SockOptsConfig{
Mark: v,
}
m["hop.so_mark"] = v
delete(m, "so_mark")
}
if v := mdutil.GetInt(md, "proxyProtocol"); v > 0 {
m["hop.proxyProtocol"] = v
delete(m, "proxyProtocol")
}
hopMd := map[string]any{}
for k, v := range m {
if strings.HasPrefix(k, "node.") ||
strings.HasPrefix(k, "connector.") ||
strings.HasPrefix(k, "dialer.") {
continue
}
if s, found := strings.CutPrefix(k, "hop."); found {
hopMd[s] = v
delete(m, k)
continue
}
hopMd[k] = v
}
hopConfig.Metadata = hopMd
nodeConfig, err := buildNodeConfig(url, m)
if err != nil {
return nil, err
}
nodeConfig.Name = fmt.Sprintf("%snode-0", namePrefix)
var nodes []*config.NodeConfig
for _, host := range strings.Split(nodeConfig.Addr, ",") {
if host == "" {
continue
}
nodeCfg := &config.NodeConfig{}
*nodeCfg = *nodeConfig
nodeCfg.Name = fmt.Sprintf("%snode-%d", namePrefix, len(nodes))
nodeCfg.Addr = host
nodes = append(nodes, nodeCfg)
}
hopConfig.Nodes = nodes
chain.Hops = append(chain.Hops, hopConfig)
}
@@ -279,7 +297,7 @@ func BuildConfigFromCmd(serviceList, nodeList []string) (*config.Config, error)
}
if in != "" || out != "" {
limiter.Limits = append(limiter.Limits,
fmt.Sprintf("%s %s %s", traffic.GlobalLimitKey, in, out))
fmt.Sprintf("%s %s %s", traffic.ServiceLimitKey, in, out))
}
if cin != "" || cout != "" {
limiter.Limits = append(limiter.Limits,
@@ -479,19 +497,63 @@ func buildServiceConfig(url *url.URL) ([]*config.ServiceConfig, error) {
}
if v := mdutil.GetString(md, "dns"); v != "" {
md.Set("dns", strings.Split(v, ","))
m["dns"] = strings.Split(v, ",")
}
if v := mdutil.GetString(md, "interface"); v != "" {
m["service.interface"] = v
delete(m, "interface")
}
if v := mdutil.GetInt(md, "so_mark"); v > 0 {
m["service.so_mark"] = v
delete(m, "so_mark")
}
if v := mdutil.GetInt(md, "proxyProtocol"); v > 0 {
m["service.proxyProtocol"] = v
delete(m, "proxyProtocol")
}
if v := mdutil.GetString(md, "netns"); v != "" {
m["service.netns"] = v
delete(m, "netns")
}
if v := mdutil.GetString(md, "netns.out"); v != "" {
m["service.netns.out"] = v
delete(m, "netns.out")
}
selector := parseSelector(m)
serviceMd := map[string]any{}
handleMd := map[string]any{}
listenerMd := map[string]any{}
for k, v := range m {
if s, found := strings.CutPrefix(k, "service."); found {
serviceMd[s] = v
continue
}
if s, found := strings.CutPrefix(k, "handler."); found {
handleMd[s] = v
continue
}
if s, found := strings.CutPrefix(k, "listener."); found {
listenerMd[s] = v
continue
}
handleMd[k] = v
listenerMd[k] = v
serviceMd[k] = v
}
handlerCfg := &config.HandlerConfig{
Type: handler,
Auth: auth,
Metadata: m,
Metadata: handleMd,
}
listenerCfg := &config.ListenerConfig{
Type: listener,
TLS: tlsConfig,
Metadata: m,
Metadata: listenerMd,
}
if listenerCfg.Type == "ssh" || listenerCfg.Type == "sshd" {
handlerCfg.Auth = nil
@@ -504,13 +566,13 @@ func buildServiceConfig(url *url.URL) ([]*config.ServiceConfig, error) {
}
svc.Handler = handlerCfg
svc.Listener = listenerCfg
svc.Metadata = m
svc.Metadata = serviceMd
}
return services, nil
}
func buildNodeConfig(url *url.URL) (*config.NodeConfig, error) {
func buildNodeConfig(url *url.URL, m map[string]any) (*config.NodeConfig, error) {
var connector, dialer string
schemes := strings.Split(url.Scheme, "+")
if len(schemes) == 1 {
@@ -522,19 +584,8 @@ func buildNodeConfig(url *url.URL) (*config.NodeConfig, error) {
dialer = schemes[1]
}
m := map[string]any{}
for k, v := range url.Query() {
if len(v) > 0 {
m[k] = v[0]
}
}
md := mdx.NewMetadata(m)
node := &config.NodeConfig{
Addr: url.Host,
Metadata: m,
}
if c := registry.ConnectorRegistry().Get(connector); c == nil {
connector = "http"
}
@@ -591,15 +642,41 @@ func buildNodeConfig(url *url.URL) (*config.NodeConfig, error) {
tlsConfig = nil
}
nodeMd := map[string]any{}
connectorMd := map[string]any{}
dialerMd := map[string]any{}
for k, v := range m {
if s, found := strings.CutPrefix(k, "node."); found {
nodeMd[s] = v
continue
}
if s, found := strings.CutPrefix(k, "connector."); found {
connectorMd[s] = v
continue
}
if s, found := strings.CutPrefix(k, "dialer."); found {
dialerMd[s] = v
continue
}
connectorMd[k] = v
dialerMd[k] = v
nodeMd[k] = v
}
node := &config.NodeConfig{
Addr: url.Host,
Metadata: nodeMd,
}
node.Connector = &config.ConnectorConfig{
Type: connector,
Auth: auth,
Metadata: m,
Metadata: connectorMd,
}
node.Dialer = &config.DialerConfig{
Type: dialer,
TLS: tlsConfig,
Metadata: m,
Metadata: dialerMd,
}
if node.Dialer.Type == "ssh" || node.Dialer.Type == "sshd" {
+77 -52
View File
@@ -143,8 +143,9 @@ type AutherConfig struct {
}
type AuthConfig struct {
Username string `json:"username"`
Username string `yaml:",omitempty" json:"username,omitempty"`
Password string `yaml:",omitempty" json:"password,omitempty"`
File string `yaml:",omitempty" json:"file,omitempty"`
}
type SelectorConfig struct {
@@ -155,7 +156,7 @@ type SelectorConfig struct {
type AdmissionConfig struct {
Name string `json:"name"`
// DEPRECATED by whitelist since beta.4
// Deprecated: use whitelist instead
Reverse bool `yaml:",omitempty" json:"reverse,omitempty"`
Whitelist bool `yaml:",omitempty" json:"whitelist,omitempty"`
Matchers []string `yaml:",omitempty" json:"matchers,omitempty"`
@@ -168,7 +169,7 @@ type AdmissionConfig struct {
type BypassConfig struct {
Name string `json:"name"`
// DEPRECATED by whitelist since beta.4
// Deprecated: use whitelist instead
Reverse bool `yaml:",omitempty" json:"reverse,omitempty"`
Whitelist bool `yaml:",omitempty" json:"whitelist,omitempty"`
Matchers []string `yaml:",omitempty" json:"matchers,omitempty"`
@@ -252,7 +253,9 @@ type SDConfig struct {
}
type RouterRouteConfig struct {
Net string `json:"net"`
// Deprecated: use dst instead
Net string `yaml:",omitempty" json:"net,omitempty"`
Dst string `yaml:",omitempty" json:"dst,omitempty"`
Gateway string `json:"gateway"`
}
@@ -276,8 +279,9 @@ type RecorderConfig struct {
}
type FileRecorder struct {
Path string `json:"path"`
Sep string `yaml:",omitempty" json:"sep,omitempty"`
Path string `json:"path"`
Sep string `yaml:",omitempty" json:"sep,omitempty"`
Rotation *LogRotationConfig `yaml:",omitempty" json:"rotation,omitempty"`
}
type TCPRecorder struct {
@@ -347,7 +351,7 @@ type HandlerConfig struct {
}
type ForwarderConfig struct {
// DEPRECATED by hop field
// Deprecated: use hop instead
Name string `yaml:",omitempty" json:"name,omitempty"`
// the referenced hop name
Hop string `yaml:",omitempty" json:"hop,omitempty"`
@@ -361,18 +365,20 @@ type ForwardNodeConfig struct {
Network string `yaml:",omitempty" json:"network,omitempty"`
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
// DEPRECATED by filter.protocol
// Deprecated: use matcher instead
Protocol string `yaml:",omitempty" json:"protocol,omitempty"`
// DEPRECATED by filter.host
// Deprecated: use matcher instead
Host string `yaml:",omitempty" json:"host,omitempty"`
// DEPRECATED by filter.path
// Deprecated: use matcher instead
Path string `yaml:",omitempty" json:"path,omitempty"`
// DEPRECATED by http.auth
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
Filter *NodeFilterConfig `yaml:",omitempty" json:"filter,omitempty"`
HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"`
TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
// Deprecated: use matcher instead
Filter *NodeFilterConfig `yaml:",omitempty" json:"filter,omitempty"`
Matcher *NodeMatcherConfig `yaml:",omitempty" json:"matcher,omitempty"`
// Deprecated: use http.auth instead
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"`
TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
}
type HTTPURLRewriteConfig struct {
@@ -393,13 +399,24 @@ type NodeFilterConfig struct {
Path string `yaml:",omitempty" json:"path,omitempty"`
}
type NodeMatcherConfig struct {
Rule string `yaml:",omitempty" json:"rule,omitempty"`
Priority int `yaml:",omitempty" json:"priority,omitempty"`
}
type HTTPNodeConfig struct {
// rewrite host header
Host string `yaml:",omitempty" json:"host,omitempty"`
// additional request header
// Deprecated: use requestHeader instead
Header map[string]string `yaml:",omitempty" json:"header,omitempty"`
// rewrite URL
// additional request header
RequestHeader map[string]string `yaml:"requestHeader,omitempty" json:"requestHeader,omitempty"`
// additional response header
ResponseHeader map[string]string `yaml:"responseHeader,omitempty" json:"responseHeader,omitempty"`
// Deprecated: use rewriteURL instead
Rewrite []HTTPURLRewriteConfig `yaml:",omitempty" json:"rewrite,omitempty"`
// rewrite URL
RewriteURL []HTTPURLRewriteConfig `yaml:"rewriteURL,omitempty" json:"rewriteURL,omitempty"`
// rewrite response body
RewriteBody []HTTPBodyRewriteConfig `yaml:"rewriteBody,omitempty" json:"rewriteBody,omitempty"`
// HTTP basic auth
@@ -433,9 +450,9 @@ type SockOptsConfig struct {
type ServiceConfig struct {
Name string `json:"name"`
Addr string `yaml:",omitempty" json:"addr,omitempty"`
// DEPRECATED by metadata.interface since beta.5
// Deprecated: use metadata.interface instead
Interface string `yaml:",omitempty" json:"interface,omitempty"`
// DEPRECATED by metadata.so_mark since beta.5
// Deprecated: use metadata.so_mark instead
SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"`
Admission string `yaml:",omitempty" json:"admission,omitempty"`
Admissions []string `yaml:",omitempty" json:"admissions,omitempty"`
@@ -490,40 +507,47 @@ type ChainGroupConfig struct {
}
type HopConfig struct {
Name string `json:"name"`
Interface string `yaml:",omitempty" json:"interface,omitempty"`
SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"`
Selector *SelectorConfig `yaml:",omitempty" json:"selector,omitempty"`
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
Nodes []*NodeConfig `yaml:",omitempty" json:"nodes,omitempty"`
Reload time.Duration `yaml:",omitempty" json:"reload,omitempty"`
File *FileLoader `yaml:",omitempty" json:"file,omitempty"`
Redis *RedisLoader `yaml:",omitempty" json:"redis,omitempty"`
HTTP *HTTPLoader `yaml:"http,omitempty" json:"http,omitempty"`
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
Name string `json:"name"`
// Deprecated: use metadata.interface instead
Interface string `yaml:",omitempty" json:"interface,omitempty"`
// Deprecated: use metadata.so_mark instead
SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"`
Selector *SelectorConfig `yaml:",omitempty" json:"selector,omitempty"`
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
Nodes []*NodeConfig `yaml:",omitempty" json:"nodes,omitempty"`
Reload time.Duration `yaml:",omitempty" json:"reload,omitempty"`
File *FileLoader `yaml:",omitempty" json:"file,omitempty"`
Redis *RedisLoader `yaml:",omitempty" json:"redis,omitempty"`
HTTP *HTTPLoader `yaml:"http,omitempty" json:"http,omitempty"`
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
}
type NodeConfig struct {
Name string `json:"name"`
Addr string `yaml:",omitempty" json:"addr,omitempty"`
Network string `yaml:",omitempty" json:"network,omitempty"`
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
Connector *ConnectorConfig `yaml:",omitempty" json:"connector,omitempty"`
Dialer *DialerConfig `yaml:",omitempty" json:"dialer,omitempty"`
Interface string `yaml:",omitempty" json:"interface,omitempty"`
Netns string `yaml:",omitempty" json:"netns,omitempty"`
SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"`
Filter *NodeFilterConfig `yaml:",omitempty" json:"filter,omitempty"`
HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"`
TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
Name string `json:"name"`
Addr string `yaml:",omitempty" json:"addr,omitempty"`
Network string `yaml:",omitempty" json:"network,omitempty"`
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
Connector *ConnectorConfig `yaml:",omitempty" json:"connector,omitempty"`
Dialer *DialerConfig `yaml:",omitempty" json:"dialer,omitempty"`
// Deprecated: use metadata.interface instead
Interface string `yaml:",omitempty" json:"interface,omitempty"`
// Deprecated: use metadata.netns instead
Netns string `yaml:",omitempty" json:"netns,omitempty"`
// Deprecated: use metadata.so_mark instead
SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"`
// Deprecated: use matcher instead
Filter *NodeFilterConfig `yaml:",omitempty" json:"filter,omitempty"`
Matcher *NodeMatcherConfig `yaml:",omitempty" json:"matcher,omitempty"`
HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"`
TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
}
type Config struct {
@@ -559,7 +583,8 @@ func (c *Config) Load() error {
return v.Unmarshal(c)
}
func (c *Config) Read(r io.Reader) error {
func (c *Config) Read(r io.Reader, configType string) error {
v.SetConfigType(configType)
if err := v.ReadConfig(r); err != nil {
return err
}
+232
View File
@@ -0,0 +1,232 @@
package loader
import (
"github.com/go-gost/core/logger"
"github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing"
admission_parser "github.com/go-gost/x/config/parsing/admission"
auth_parser "github.com/go-gost/x/config/parsing/auth"
bypass_parser "github.com/go-gost/x/config/parsing/bypass"
chain_parser "github.com/go-gost/x/config/parsing/chain"
hop_parser "github.com/go-gost/x/config/parsing/hop"
hosts_parser "github.com/go-gost/x/config/parsing/hosts"
ingress_parser "github.com/go-gost/x/config/parsing/ingress"
limiter_parser "github.com/go-gost/x/config/parsing/limiter"
logger_parser "github.com/go-gost/x/config/parsing/logger"
observer_parser "github.com/go-gost/x/config/parsing/observer"
recorder_parser "github.com/go-gost/x/config/parsing/recorder"
resolver_parser "github.com/go-gost/x/config/parsing/resolver"
router_parser "github.com/go-gost/x/config/parsing/router"
sd_parser "github.com/go-gost/x/config/parsing/sd"
service_parser "github.com/go-gost/x/config/parsing/service"
"github.com/go-gost/x/registry"
)
var (
defaultLoader *loader = &loader{}
)
func Load(cfg *config.Config) error {
return defaultLoader.Load(cfg)
}
type loader struct{}
func (l *loader) Load(cfg *config.Config) error {
logCfg := cfg.Log
if logCfg == nil {
logCfg = &config.LogConfig{}
}
logger.SetDefault(logger_parser.ParseLogger(&config.LoggerConfig{Log: logCfg}))
tlsCfg, err := parsing.BuildDefaultTLSConfig(cfg.TLS)
if err != nil {
return err
}
parsing.SetDefaultTLSConfig(tlsCfg)
if err := register(cfg); err != nil {
return err
}
return nil
}
func register(cfg *config.Config) error {
if cfg == nil {
return nil
}
for name := range registry.LoggerRegistry().GetAll() {
registry.LoggerRegistry().Unregister(name)
}
for _, loggerCfg := range cfg.Loggers {
if err := registry.LoggerRegistry().Register(loggerCfg.Name, logger_parser.ParseLogger(loggerCfg)); err != nil {
return err
}
}
for name := range registry.AutherRegistry().GetAll() {
registry.AutherRegistry().Unregister(name)
}
for _, autherCfg := range cfg.Authers {
if err := registry.AutherRegistry().Register(autherCfg.Name, auth_parser.ParseAuther(autherCfg)); err != nil {
return err
}
}
for name := range registry.AdmissionRegistry().GetAll() {
registry.AdmissionRegistry().Unregister(name)
}
for _, admissionCfg := range cfg.Admissions {
if err := registry.AdmissionRegistry().Register(admissionCfg.Name, admission_parser.ParseAdmission(admissionCfg)); err != nil {
return err
}
}
for name := range registry.BypassRegistry().GetAll() {
registry.BypassRegistry().Unregister(name)
}
for _, bypassCfg := range cfg.Bypasses {
if err := registry.BypassRegistry().Register(bypassCfg.Name, bypass_parser.ParseBypass(bypassCfg)); err != nil {
return err
}
}
for name := range registry.ResolverRegistry().GetAll() {
registry.ResolverRegistry().Unregister(name)
}
for _, resolverCfg := range cfg.Resolvers {
r, err := resolver_parser.ParseResolver(resolverCfg)
if err != nil {
return err
}
if err := registry.ResolverRegistry().Register(resolverCfg.Name, r); err != nil {
return err
}
}
for name := range registry.HostsRegistry().GetAll() {
registry.HostsRegistry().Unregister(name)
}
for _, hostsCfg := range cfg.Hosts {
if err := registry.HostsRegistry().Register(hostsCfg.Name, hosts_parser.ParseHostMapper(hostsCfg)); err != nil {
return err
}
}
for name := range registry.IngressRegistry().GetAll() {
registry.IngressRegistry().Unregister(name)
}
for _, ingressCfg := range cfg.Ingresses {
if err := registry.IngressRegistry().Register(ingressCfg.Name, ingress_parser.ParseIngress(ingressCfg)); err != nil {
return err
}
}
for name := range registry.RouterRegistry().GetAll() {
registry.RouterRegistry().Unregister(name)
}
for _, routerCfg := range cfg.Routers {
if err := registry.RouterRegistry().Register(routerCfg.Name, router_parser.ParseRouter(routerCfg)); err != nil {
return err
}
}
for name := range registry.SDRegistry().GetAll() {
registry.SDRegistry().Unregister(name)
}
for _, sdCfg := range cfg.SDs {
if err := registry.SDRegistry().Register(sdCfg.Name, sd_parser.ParseSD(sdCfg)); err != nil {
return err
}
}
for name := range registry.ObserverRegistry().GetAll() {
registry.ObserverRegistry().Unregister(name)
}
for _, observerCfg := range cfg.Observers {
if err := registry.ObserverRegistry().Register(observerCfg.Name, observer_parser.ParseObserver(observerCfg)); err != nil {
return err
}
}
for name := range registry.RecorderRegistry().GetAll() {
registry.RecorderRegistry().Unregister(name)
}
for _, recorderCfg := range cfg.Recorders {
if err := registry.RecorderRegistry().Register(recorderCfg.Name, recorder_parser.ParseRecorder(recorderCfg)); err != nil {
return err
}
}
for name := range registry.TrafficLimiterRegistry().GetAll() {
registry.TrafficLimiterRegistry().Unregister(name)
}
for _, limiterCfg := range cfg.Limiters {
if err := registry.TrafficLimiterRegistry().Register(limiterCfg.Name, limiter_parser.ParseTrafficLimiter(limiterCfg)); err != nil {
return err
}
}
for name := range registry.ConnLimiterRegistry().GetAll() {
registry.ConnLimiterRegistry().Unregister(name)
}
for _, limiterCfg := range cfg.CLimiters {
if err := registry.ConnLimiterRegistry().Register(limiterCfg.Name, limiter_parser.ParseConnLimiter(limiterCfg)); err != nil {
return err
}
}
for name := range registry.RateLimiterRegistry().GetAll() {
registry.RateLimiterRegistry().Unregister(name)
}
for _, limiterCfg := range cfg.RLimiters {
if err := registry.RateLimiterRegistry().Register(limiterCfg.Name, limiter_parser.ParseRateLimiter(limiterCfg)); err != nil {
return err
}
}
for name := range registry.HopRegistry().GetAll() {
registry.HopRegistry().Unregister(name)
}
for _, hopCfg := range cfg.Hops {
hop, err := hop_parser.ParseHop(hopCfg, logger.Default())
if err != nil {
return err
}
if err := registry.HopRegistry().Register(hopCfg.Name, hop); err != nil {
return err
}
}
for name := range registry.ChainRegistry().GetAll() {
registry.ChainRegistry().Unregister(name)
}
for _, chainCfg := range cfg.Chains {
c, err := chain_parser.ParseChain(chainCfg, logger.Default())
if err != nil {
return err
}
if err := registry.ChainRegistry().Register(chainCfg.Name, c); err != nil {
return err
}
}
for name := range registry.ServiceRegistry().GetAll() {
registry.ServiceRegistry().Unregister(name)
}
for _, svcCfg := range cfg.Services {
svc, err := service_parser.ParseService(svcCfg)
if err != nil {
return err
}
if svc != nil {
if err := registry.ServiceRegistry().Register(svcCfg.Name, svc); err != nil {
return err
}
}
}
return nil
}
+51 -1
View File
@@ -1,8 +1,12 @@
package auth
import (
"bufio"
"crypto/tls"
"io"
"net/url"
"os"
"strings"
"github.com/go-gost/core/auth"
"github.com/go-gost/core/logger"
@@ -98,7 +102,20 @@ func ParseAutherFromAuth(au *config.AuthConfig) auth.Authenticator {
}
func Info(cfg *config.AuthConfig) *url.Userinfo {
if cfg == nil || cfg.Username == "" {
if cfg == nil {
return nil
}
if cfg.File != "" {
if f, _ := os.Open(cfg.File); f != nil {
defer f.Close()
if infos, _ := parseInfo(f, 1); len(infos) > 0 {
return infos[0]
}
}
}
if cfg.Username == "" {
return nil
}
@@ -108,6 +125,39 @@ func Info(cfg *config.AuthConfig) *url.Userinfo {
return url.UserPassword(cfg.Username, cfg.Password)
}
func parseInfo(r io.Reader, max int) (infos []*url.Userinfo, err error) {
if r == nil {
return
}
scanner := bufio.NewScanner(r)
for scanner.Scan() {
// line := strings.Replace(scanner.Text(), "\t", " ", -1)
line := strings.TrimSpace(scanner.Text())
if n := strings.IndexByte(line, '#'); n == 0 {
continue
}
sp := strings.SplitN(line, " ", 2)
if len(sp) == 1 {
if k := strings.TrimSpace(sp[0]); k != "" {
infos = append(infos, url.User(k))
}
}
if len(sp) == 2 {
if k := strings.TrimSpace(sp[0]); k != "" {
infos = append(infos, url.UserPassword(k, strings.TrimSpace(sp[1])))
}
}
if max > 0 && len(infos) >= max {
break
}
}
err = scanner.Err()
return
}
func List(name string, names ...string) []auth.Authenticator {
var authers []auth.Authenticator
if auther := registry.AutherRegistry().Get(name); auther != nil {
+53 -11
View File
@@ -4,10 +4,10 @@ import (
"crypto/tls"
"strings"
"github.com/go-gost/core/bypass"
"github.com/go-gost/core/chain"
"github.com/go-gost/core/hop"
"github.com/go-gost/core/logger"
xbypass "github.com/go-gost/x/bypass"
"github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing"
bypass_parser "github.com/go-gost/x/config/parsing/bypass"
@@ -50,6 +50,8 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) {
}
}
var ppv int
var soMark int
ifce := cfg.Interface
var netns string
if cfg.Metadata != nil {
@@ -57,7 +59,15 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) {
if v := mdutil.GetString(md, parsing.MDKeyInterface); v != "" {
ifce = v
}
netns = mdutil.GetString(md, "netns")
if cfg.SockOpts != nil {
soMark = cfg.SockOpts.Mark
}
if v := mdutil.GetInt(md, parsing.MDKeySoMark); v > 0 {
soMark = v
}
ppv = mdutil.GetInt(md, parsing.MDKeyProxyProtocol)
netns = mdutil.GetString(md, parsing.MDKeyNetns)
}
var nodes []*chain.Node
@@ -66,21 +76,53 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) {
continue
}
m := v.Metadata
if m == nil {
m = map[string]any{}
v.Metadata = m
}
md := metadata.NewMetadata(m)
if v.Resolver == "" {
v.Resolver = cfg.Resolver
}
if v.Hosts == "" {
v.Hosts = cfg.Hosts
}
if v.Interface == "" {
v.Interface = ifce
}
if v.Netns == "" {
v.Netns = netns
}
if v.SockOpts == nil {
v.SockOpts = cfg.SockOpts
if !md.IsExists(parsing.MDKeyInterface) {
// inherit from hop
if ifce != "" {
m[parsing.MDKeyInterface] = ifce
}
// node level
if v.Interface != "" {
m[parsing.MDKeyInterface] = v.Interface
}
}
if !md.IsExists(parsing.MDKeySoMark) {
// inherit from hop
if soMark != 0 {
m[parsing.MDKeySoMark] = soMark
}
// node level
if v.SockOpts != nil && v.SockOpts.Mark != 0 {
m[parsing.MDKeySoMark] = v.SockOpts.Mark
}
}
if !md.IsExists(parsing.MDKeyProxyProtocol) && ppv > 0 {
// inherit from hop
m[parsing.MDKeyProxyProtocol] = ppv
}
if !md.IsExists(parsing.MDKeyNetns) {
// inherit from hop
if netns != "" {
m[parsing.MDKeyNetns] = netns
}
// node level
if v.Netns != "" {
m[parsing.MDKeyNetns] = v.Name
}
}
if v.Connector == nil {
@@ -115,7 +157,7 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) {
xhop.NameOption(cfg.Name),
xhop.NodeOption(nodes...),
xhop.SelectorOption(sel),
xhop.BypassOption(bypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
xhop.BypassOption(xbypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
xhop.ReloadPeriodOption(cfg.Reload),
xhop.LoggerOption(log.WithFields(map[string]any{
"kind": "hop",
+1 -1
View File
@@ -25,7 +25,7 @@ func ParseLogger(cfg *config.LoggerConfig) logger.Logger {
var out io.Writer = os.Stderr
switch cfg.Log.Output {
case "none", "null":
return xlogger.Nop()
out = io.Discard
case "stdout":
out = os.Stdout
case "stderr", "":
+43 -23
View File
@@ -6,14 +6,12 @@ import (
"regexp"
"strings"
"github.com/go-gost/core/bypass"
"github.com/go-gost/core/chain"
"github.com/go-gost/core/connector"
"github.com/go-gost/core/dialer"
"github.com/go-gost/core/logger"
"github.com/go-gost/core/metadata"
mdutil "github.com/go-gost/x/metadata/util"
xauth "github.com/go-gost/x/auth"
xbypass "github.com/go-gost/x/bypass"
xchain "github.com/go-gost/x/chain"
"github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing"
@@ -21,7 +19,9 @@ import (
bypass_parser "github.com/go-gost/x/config/parsing/bypass"
tls_util "github.com/go-gost/x/internal/util/tls"
mdx "github.com/go-gost/x/metadata"
mdutil "github.com/go-gost/x/metadata/util"
"github.com/go-gost/x/registry"
"github.com/go-gost/x/routing"
)
func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.Node, error) {
@@ -64,11 +64,6 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
return nil, err
}
var nm metadata.Metadata
if cfg.Metadata != nil {
nm = mdx.NewMetadata(cfg.Metadata)
}
connectorLogger := nodeLogger.WithFields(map[string]any{
"kind": "connector",
})
@@ -104,10 +99,7 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
return nil, err
}
var ppv int
if nm != nil {
ppv = mdutil.GetInt(nm, parsing.MDKeyProxyProtocol)
}
md := mdx.NewMetadata(cfg.Metadata)
dialerLogger := nodeLogger.WithFields(map[string]any{
"kind": "dialer",
@@ -119,7 +111,7 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
dialer.AuthOption(auth_parser.Info(cfg.Dialer.Auth)),
dialer.TLSConfigOption(tlsConfig),
dialer.LoggerOption(dialerLogger),
dialer.ProxyProtocolOption(ppv),
dialer.ProxyProtocolOption(mdutil.GetInt(md, parsing.MDKeyProxyProtocol)),
)
} else {
return nil, fmt.Errorf("unregistered dialer: %s", cfg.Dialer.Type)
@@ -134,25 +126,25 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
}
var sockOpts *chain.SockOpts
if cfg.SockOpts != nil {
if v := mdutil.GetInt(md, parsing.MDKeySoMark); v != 0 {
sockOpts = &chain.SockOpts{
Mark: cfg.SockOpts.Mark,
Mark: v,
}
}
tr := xchain.NewTransport(d, cr,
chain.AddrTransportOption(cfg.Addr),
chain.InterfaceTransportOption(cfg.Interface),
chain.NetnsTransportOption(cfg.Netns),
chain.InterfaceTransportOption(mdutil.GetString(md, parsing.MDKeyInterface)),
chain.NetnsTransportOption(mdutil.GetString(md, parsing.MDKeyNetns)),
chain.SockOptsTransportOption(sockOpts),
)
opts := []chain.NodeOption{
chain.TransportNodeOption(tr),
chain.BypassNodeOption(bypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
chain.BypassNodeOption(xbypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
chain.ResoloverNodeOption(registry.ResolverRegistry().Get(cfg.Resolver)),
chain.HostMapperNodeOption(registry.HostsRegistry().Get(cfg.Hosts)),
chain.MetadataNodeOption(nm),
chain.MetadataNodeOption(md),
chain.NetworkNodeOption(cfg.Network),
}
@@ -175,10 +167,33 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
opts = append(opts, chain.NodeFilterOption(settings))
}
if cfg.Matcher != nil {
priority := cfg.Matcher.Priority
if rule := strings.TrimSpace(cfg.Matcher.Rule); rule != "" {
if matcher, err := routing.NewMatcher(rule); err == nil {
log.Debugf("new matcher for node %s with rule %s", cfg.Name, cfg.Matcher.Rule)
if priority == 0 {
priority = len(cfg.Matcher.Rule)
}
opts = append(opts, chain.MatcherNodeOption(matcher))
} else {
log.Error(err)
priority = -1
}
}
opts = append(opts, chain.PriorityNodeOption(priority))
}
if cfg.HTTP != nil {
settings := &chain.HTTPNodeSettings{
Host: cfg.HTTP.Host,
Header: cfg.HTTP.Header,
Host: cfg.HTTP.Host,
RequestHeader: cfg.HTTP.RequestHeader,
ResponseHeader: cfg.HTTP.ResponseHeader,
}
if settings.RequestHeader == nil {
settings.RequestHeader = cfg.HTTP.Header
}
if auth := cfg.HTTP.Auth; auth != nil && auth.Username != "" {
@@ -191,7 +206,12 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
})),
)
}
for _, v := range cfg.HTTP.Rewrite {
rewriteURL := cfg.HTTP.RewriteURL
if rewriteURL == nil {
rewriteURL = cfg.HTTP.Rewrite
}
for _, v := range rewriteURL {
if pattern, _ := regexp.Compile(v.Match); pattern != nil {
settings.RewriteURL = append(settings.RewriteURL, chain.HTTPURLRewriteSetting{
Pattern: pattern,
@@ -201,7 +221,7 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
}
for _, v := range cfg.HTTP.RewriteBody {
if pattern, _ := regexp.Compile(v.Match); pattern != nil {
settings.RewriteBody = append(settings.RewriteBody, chain.HTTPBodyRewriteSettings{
settings.RewriteResponseBody = append(settings.RewriteResponseBody, chain.HTTPBodyRewriteSettings{
Type: v.Type,
Pattern: pattern,
Replacement: []byte(v.Replacement),
+12
View File
@@ -17,4 +17,16 @@ const (
MDKeyRecorderHexdump = "hexdump"
MDKeyRecorderHTTPBody = "http.body"
MDKeyRecorderHTTPMaxBodySize = "http.maxBodySize"
MDKeyLimiterRefreshInterval = "limiter.refreshInterval"
MDKeyLimiterCleanupInterval = "limiter.cleanupInterval"
MDKeyLimiterScope = "limiter.scope"
MDKeyObserverResetTraffic = "observer.resetTraffic"
MDKeyObserverPeriod = "observer.period"
MDKeyNetns = "netns"
MDKeyNetnsOut = "netns.out"
MDKeyDialTimeout = "dialTimeout"
)
+220
View File
@@ -0,0 +1,220 @@
package parser
import (
"bufio"
"encoding/json"
"os"
"strings"
"github.com/go-gost/core/logger"
"github.com/go-gost/x/config"
"github.com/go-gost/x/config/cmd"
xmd "github.com/go-gost/x/metadata"
mdutil "github.com/go-gost/x/metadata/util"
)
var (
defaultParser = &parser{}
)
func Init(args Args) {
defaultParser = &parser{
args: args,
}
}
func Parse() (*config.Config, error) {
return defaultParser.Parse()
}
type Args struct {
CfgFile string
Services []string
Nodes []string
Debug bool
Trace bool
ApiAddr string
MetricsAddr string
}
type parser struct {
args Args
}
func (p *parser) Parse() (*config.Config, error) {
cfg := &config.Config{}
if cfgFile := strings.TrimSpace(p.args.CfgFile); cfgFile != "" {
if cfgFile == "-" { // stdin
br := bufio.NewReader(os.Stdin)
b, err := br.Peek(1)
if err != nil {
return nil, err
}
if b[0] == '{' {
if err := cfg.Read(br, "json"); err != nil {
return nil, err
}
} else {
if err := cfg.Read(br, "yaml"); err != nil {
return nil, err
}
}
} else if strings.HasPrefix(cfgFile, "{") && strings.HasSuffix(cfgFile, "}") { // inline
if err := json.Unmarshal([]byte(cfgFile), cfg); err != nil {
return nil, err
}
} else {
if err := cfg.ReadFile(cfgFile); err != nil { // file
return nil, err
}
}
}
cmdCfg, err := cmd.BuildConfigFromCmd(p.args.Services, p.args.Nodes)
if err != nil {
return nil, err
}
cfg = mergeConfig(cfg, cmdCfg)
if len(cfg.Services) == 0 && p.args.ApiAddr == "" && cfg.API == nil {
if err := cfg.Load(); err != nil {
return nil, err
}
}
if v := os.Getenv("GOST_LOGGER_LEVEL"); v != "" {
cfg.Log = &config.LogConfig{
Level: v,
}
}
if v := os.Getenv("GOST_API"); v != "" {
cfg.API = &config.APIConfig{
Addr: v,
}
}
if v := os.Getenv("GOST_METRICS"); v != "" {
cfg.Metrics = &config.MetricsConfig{
Addr: v,
}
}
if v := os.Getenv("GOST_PROFILING"); v != "" {
cfg.Profiling = &config.ProfilingConfig{
Addr: v,
}
}
if p.args.Debug || p.args.Trace {
if cfg.Log == nil {
cfg.Log = &config.LogConfig{}
}
cfg.Log.Level = string(logger.DebugLevel)
if p.args.Trace {
cfg.Log.Level = string(logger.TraceLevel)
}
}
if p.args.ApiAddr != "" {
cfg.API = &config.APIConfig{
Addr: p.args.ApiAddr,
}
if url, _ := cmd.Norm(p.args.ApiAddr); url != nil {
cfg.API.Addr = url.Host
if url.User != nil {
username := url.User.Username()
password, _ := url.User.Password()
cfg.API.Auth = &config.AuthConfig{
Username: username,
Password: password,
}
}
m := map[string]any{}
for k, v := range url.Query() {
if len(v) > 0 {
m[k] = v[0]
}
}
md := xmd.NewMetadata(m)
cfg.API.PathPrefix = mdutil.GetString(md, "pathPrefix")
cfg.API.AccessLog = mdutil.GetBool(md, "accesslog")
}
}
if p.args.MetricsAddr != "" {
cfg.Metrics = &config.MetricsConfig{
Addr: p.args.MetricsAddr,
}
if url, _ := cmd.Norm(p.args.MetricsAddr); url != nil {
cfg.Metrics.Addr = url.Host
if url.User != nil {
username := url.User.Username()
password, _ := url.User.Password()
cfg.Metrics.Auth = &config.AuthConfig{
Username: username,
Password: password,
}
}
m := map[string]any{}
for k, v := range url.Query() {
if len(v) > 0 {
m[k] = v[0]
}
}
md := xmd.NewMetadata(m)
cfg.Metrics.Path = mdutil.GetString(md, "path")
}
}
return cfg, nil
}
func mergeConfig(cfg1, cfg2 *config.Config) *config.Config {
if cfg1 == nil {
return cfg2
}
if cfg2 == nil {
return cfg1
}
cfg := &config.Config{
Services: append(cfg1.Services, cfg2.Services...),
Chains: append(cfg1.Chains, cfg2.Chains...),
Hops: append(cfg1.Hops, cfg2.Hops...),
Authers: append(cfg1.Authers, cfg2.Authers...),
Admissions: append(cfg1.Admissions, cfg2.Admissions...),
Bypasses: append(cfg1.Bypasses, cfg2.Bypasses...),
Resolvers: append(cfg1.Resolvers, cfg2.Resolvers...),
Hosts: append(cfg1.Hosts, cfg2.Hosts...),
Ingresses: append(cfg1.Ingresses, cfg2.Ingresses...),
SDs: append(cfg1.SDs, cfg2.SDs...),
Recorders: append(cfg1.Recorders, cfg2.Recorders...),
Limiters: append(cfg1.Limiters, cfg2.Limiters...),
CLimiters: append(cfg1.CLimiters, cfg2.CLimiters...),
RLimiters: append(cfg1.RLimiters, cfg2.RLimiters...),
Loggers: append(cfg1.Loggers, cfg2.Loggers...),
Routers: append(cfg1.Routers, cfg2.Routers...),
Observers: append(cfg1.Observers, cfg2.Observers...),
TLS: cfg1.TLS,
Log: cfg1.Log,
API: cfg1.API,
Metrics: cfg1.Metrics,
Profiling: cfg1.Profiling,
}
if cfg2.TLS != nil {
cfg.TLS = cfg2.TLS
}
if cfg2.Log != nil {
cfg.Log = cfg2.Log
}
if cfg2.API != nil {
cfg.API = cfg2.API
}
if cfg2.Metrics != nil {
cfg.Metrics = cfg2.Metrics
}
if cfg2.Profiling != nil {
cfg.Profiling = cfg2.Profiling
}
return cfg
}
+42 -3
View File
@@ -2,16 +2,26 @@ package recorder
import (
"crypto/tls"
"io"
"net/http"
"os"
"path/filepath"
"strings"
"github.com/go-gost/core/logger"
"github.com/go-gost/core/recorder"
"github.com/go-gost/x/config"
"github.com/go-gost/x/internal/plugin"
xrecorder "github.com/go-gost/x/recorder"
recorder_plugin "github.com/go-gost/x/recorder/plugin"
"gopkg.in/natefinch/lumberjack.v2"
)
type discardCloser struct{}
func (discardCloser) Write(p []byte) (n int, err error) { return len(p), nil }
func (discardCloser) Close() error { return nil }
func ParseRecorder(cfg *config.RecorderConfig) (r recorder.Recorder) {
if cfg == nil {
return nil
@@ -42,13 +52,38 @@ func ParseRecorder(cfg *config.RecorderConfig) (r recorder.Recorder) {
}
if cfg.File != nil && cfg.File.Path != "" {
return xrecorder.FileRecorder(cfg.File.Path,
xrecorder.SepRecorderOption(cfg.File.Sep),
var out io.WriteCloser = discardCloser{}
if cfg.File.Rotation != nil {
out = &lumberjack.Logger{
Filename: cfg.File.Path,
MaxSize: cfg.File.Rotation.MaxSize,
MaxAge: cfg.File.Rotation.MaxAge,
MaxBackups: cfg.File.Rotation.MaxBackups,
LocalTime: cfg.File.Rotation.LocalTime,
Compress: cfg.File.Rotation.Compress,
}
} else {
os.MkdirAll(filepath.Dir(cfg.File.Path), 0755)
f, err := os.OpenFile(cfg.File.Path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
logger.Default().Warn(err)
} else {
out = f
}
}
return xrecorder.FileRecorder(out,
xrecorder.RecorderFileRecorderOption(cfg.Name),
xrecorder.SepFileRecorderOption(cfg.File.Sep),
)
}
if cfg.TCP != nil && cfg.TCP.Addr != "" {
return xrecorder.TCPRecorder(cfg.TCP.Addr, xrecorder.TimeoutTCPRecorderOption(cfg.TCP.Timeout))
return xrecorder.TCPRecorder(cfg.TCP.Addr,
xrecorder.RecorderTCPRecorderOption(cfg.Name),
xrecorder.TimeoutTCPRecorderOption(cfg.TCP.Timeout),
)
}
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
@@ -57,6 +92,7 @@ func ParseRecorder(cfg *config.RecorderConfig) (r recorder.Recorder) {
h.Add(k, v)
}
return xrecorder.HTTPRecorder(cfg.HTTP.URL,
xrecorder.RecorderHTTPRecorderOption(cfg.Name),
xrecorder.TimeoutHTTPRecorderOption(cfg.HTTP.Timeout),
xrecorder.HeaderHTTPRecorderOption(h),
)
@@ -68,6 +104,7 @@ func ParseRecorder(cfg *config.RecorderConfig) (r recorder.Recorder) {
switch cfg.Redis.Type {
case "list": // redis list
return xrecorder.RedisListRecorder(cfg.Redis.Addr,
xrecorder.RecorderRedisRecorderOption(cfg.Name),
xrecorder.DBRedisRecorderOption(cfg.Redis.DB),
xrecorder.KeyRedisRecorderOption(cfg.Redis.Key),
xrecorder.UsernameRedisRecorderOption(cfg.Redis.Username),
@@ -75,6 +112,7 @@ func ParseRecorder(cfg *config.RecorderConfig) (r recorder.Recorder) {
)
case "sset": // sorted set
return xrecorder.RedisSortedSetRecorder(cfg.Redis.Addr,
xrecorder.RecorderRedisRecorderOption(cfg.Name),
xrecorder.DBRedisRecorderOption(cfg.Redis.DB),
xrecorder.KeyRedisRecorderOption(cfg.Redis.Key),
xrecorder.UsernameRedisRecorderOption(cfg.Redis.Username),
@@ -82,6 +120,7 @@ func ParseRecorder(cfg *config.RecorderConfig) (r recorder.Recorder) {
)
default: // redis set
return xrecorder.RedisSetRecorder(cfg.Redis.Addr,
xrecorder.RecorderRedisRecorderOption(cfg.Name),
xrecorder.DBRedisRecorderOption(cfg.Redis.DB),
xrecorder.KeyRedisRecorderOption(cfg.Redis.Key),
xrecorder.UsernameRedisRecorderOption(cfg.Redis.Username),
+14 -5
View File
@@ -45,18 +45,27 @@ func ParseRouter(cfg *config.RouterConfig) router.Router {
var routes []*router.Route
for _, route := range cfg.Routes {
_, ipNet, _ := net.ParseCIDR(route.Net)
if ipNet == nil {
if route == nil {
continue
}
gw := net.ParseIP(route.Gateway)
if gw == nil {
_, ipNet, _ := net.ParseCIDR(route.Net)
dst := route.Dst
if dst != "" {
_, ipNet, _ = net.ParseCIDR(dst)
} else {
if ipNet != nil {
dst = ipNet.String()
}
}
if dst == "" || route.Gateway == "" {
continue
}
routes = append(routes, &router.Route{
Net: ipNet,
Gateway: gw,
Dst: dst,
Gateway: route.Gateway,
})
}
opts := []xrouter.Option{
+38 -16
View File
@@ -6,9 +6,7 @@ import (
"strings"
"time"
"github.com/go-gost/core/admission"
"github.com/go-gost/core/auth"
"github.com/go-gost/core/bypass"
"github.com/go-gost/core/chain"
"github.com/go-gost/core/handler"
"github.com/go-gost/core/hop"
@@ -18,6 +16,9 @@ import (
"github.com/go-gost/core/recorder"
"github.com/go-gost/core/selector"
"github.com/go-gost/core/service"
xadmission "github.com/go-gost/x/admission"
xauth "github.com/go-gost/x/auth"
xbypass "github.com/go-gost/x/bypass"
xchain "github.com/go-gost/x/chain"
"github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing"
@@ -28,8 +29,10 @@ import (
logger_parser "github.com/go-gost/x/config/parsing/logger"
selector_parser "github.com/go-gost/x/config/parsing/selector"
tls_util "github.com/go-gost/x/internal/util/tls"
cache_limiter "github.com/go-gost/x/limiter/traffic/cache"
"github.com/go-gost/x/metadata"
mdutil "github.com/go-gost/x/metadata/util"
xstats "github.com/go-gost/x/observer/stats"
"github.com/go-gost/x/registry"
xservice "github.com/go-gost/x/service"
"github.com/vishvananda/netns"
@@ -84,7 +87,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
}
var auther auth.Authenticator
if len(authers) > 0 {
auther = auth.AuthenticatorGroup(authers...)
auther = xauth.AuthenticatorGroup(authers...)
}
admissions := admission_parser.List(cfg.Admission, cfg.Admissions...)
@@ -100,10 +103,15 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
ifce := cfg.Interface
var preUp, preDown, postUp, postDown []string
var ignoreChain bool
var pStats *stats.Stats
var observePeriod time.Duration
var pStats stats.Stats
var observerPeriod time.Duration
var netnsIn, netnsOut string
var dialTimeout time.Duration
var limiterRefreshInterval time.Duration
var limiterCleanupInterval time.Duration
var limiterScope string
if cfg.Metadata != nil {
md := metadata.NewMetadata(cfg.Metadata)
ppv = mdutil.GetInt(md, parsing.MDKeyProxyProtocol)
@@ -122,12 +130,18 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
ignoreChain = mdutil.GetBool(md, parsing.MDKeyIgnoreChain)
if mdutil.GetBool(md, parsing.MDKeyEnableStats) {
pStats = &stats.Stats{}
pStats = xstats.NewStats(mdutil.GetBool(md, parsing.MDKeyObserverResetTraffic))
}
observePeriod = mdutil.GetDuration(md, "observePeriod")
netnsIn = mdutil.GetString(md, "netns")
netnsOut = mdutil.GetString(md, "netns.out")
dialTimeout = mdutil.GetDuration(md, "dialTimeout")
observerPeriod = mdutil.GetDuration(md, parsing.MDKeyObserverPeriod, "observePeriod")
netnsIn = mdutil.GetString(md, parsing.MDKeyNetns)
netnsOut = mdutil.GetString(md, parsing.MDKeyNetnsOut)
dialTimeout = mdutil.GetDuration(md, parsing.MDKeyDialTimeout)
limiterRefreshInterval = mdutil.GetDuration(md, parsing.MDKeyLimiterRefreshInterval)
limiterCleanupInterval = mdutil.GetDuration(md, parsing.MDKeyLimiterCleanupInterval)
limiterScope = mdutil.GetString(md, parsing.MDKeyLimiterScope)
}
listenerLogger := serviceLogger.WithFields(map[string]any{
@@ -155,8 +169,15 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
listener.AutherOption(auther),
listener.AuthOption(auth_parser.Info(cfg.Listener.Auth)),
listener.TLSConfigOption(tlsConfig),
listener.AdmissionOption(admission.AdmissionGroup(admissions...)),
listener.TrafficLimiterOption(registry.TrafficLimiterRegistry().Get(cfg.Limiter)),
listener.AdmissionOption(xadmission.AdmissionGroup(admissions...)),
listener.TrafficLimiterOption(
cache_limiter.NewCachedTrafficLimiter(
registry.TrafficLimiterRegistry().Get(cfg.Limiter),
cache_limiter.RefreshIntervalOption(limiterRefreshInterval),
cache_limiter.CleanupIntervalOption(limiterCleanupInterval),
cache_limiter.ScopeOption(limiterScope),
),
),
listener.ConnLimiterOption(registry.ConnLimiterRegistry().Get(cfg.CLimiter)),
listener.ServiceOption(cfg.Name),
listener.ProxyProtocolOption(ppv),
@@ -235,7 +256,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
auther = nil
if len(authers) > 0 {
auther = auth.AuthenticatorGroup(authers...)
auther = xauth.AuthenticatorGroup(authers...)
}
var recorders []recorder.RecorderObject
@@ -277,7 +298,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
handler.RouterOption(xchain.NewRouter(routerOpts...)),
handler.AutherOption(auther),
handler.AuthOption(auth_parser.Info(cfg.Handler.Auth)),
handler.BypassOption(bypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
handler.BypassOption(xbypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
handler.TLSConfigOption(tlsConfig),
handler.RateLimiterOption(registry.RateLimiterRegistry().Get(cfg.RLimiter)),
handler.TrafficLimiterOption(registry.TrafficLimiterRegistry().Get(cfg.Handler.Limiter)),
@@ -309,7 +330,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
}
s := xservice.NewService(cfg.Name, ln, h,
xservice.AdmissionOption(admission.AdmissionGroup(admissions...)),
xservice.AdmissionOption(xadmission.AdmissionGroup(admissions...)),
xservice.PreUpOption(preUp),
xservice.PreDownOption(preDown),
xservice.PostUpOption(postUp),
@@ -317,7 +338,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
xservice.RecordersOption(recorders...),
xservice.StatsOption(pStats),
xservice.ObserverOption(registry.ObserverRegistry().Get(cfg.Observer)),
xservice.ObservePeriodOption(observePeriod),
xservice.ObserverPeriodOption(observerPeriod),
xservice.LoggerOption(serviceLogger),
)
@@ -374,6 +395,7 @@ func parseForwarder(cfg *config.ForwarderConfig, log logger.Logger) (hop.Hop, er
Bypass: node.Bypass,
Bypasses: node.Bypasses,
Filter: filter,
Matcher: node.Matcher,
HTTP: httpCfg,
TLS: node.TLS,
Metadata: node.Metadata,
+12 -5
View File
@@ -12,6 +12,7 @@ import (
"crypto/x509/pkix"
"encoding/pem"
"math/big"
"sync/atomic"
"time"
"github.com/go-gost/core/logger"
@@ -20,14 +21,19 @@ import (
)
var (
defaultTLSConfig *tls.Config
defaultTLSConfig atomic.Value
)
func DefaultTLSConfig() *tls.Config {
return defaultTLSConfig
v, _ := defaultTLSConfig.Load().(*tls.Config)
return v
}
func BuildDefaultTLSConfig(cfg *config.TLSConfig) {
func SetDefaultTLSConfig(cfg *tls.Config) {
defaultTLSConfig.Store(cfg)
}
func BuildDefaultTLSConfig(cfg *config.TLSConfig) (*tls.Config, error) {
log := logger.Default()
if cfg == nil {
@@ -43,7 +49,7 @@ func BuildDefaultTLSConfig(cfg *config.TLSConfig) {
// generate random self-signed certificate.
cert, err := genCertificate(cfg.Validity, cfg.Organization, cfg.CommonName)
if err != nil {
log.Fatal(err)
return nil, err
}
tlsConfig = &tls.Config{
Certificates: []tls.Certificate{cert},
@@ -52,7 +58,8 @@ func BuildDefaultTLSConfig(cfg *config.TLSConfig) {
} else {
log.Debug("load global TLS certificate files OK")
}
defaultTLSConfig = tlsConfig
return tlsConfig, nil
}
func genCertificate(validity time.Duration, org string, cn string) (cert tls.Certificate, err error) {
+41
View File
@@ -0,0 +1,41 @@
package direct
import (
"io"
"net"
"time"
)
type conn struct{}
func (c *conn) Close() error {
return nil
}
func (c *conn) Read(b []byte) (n int, err error) {
return 0, io.EOF
}
func (c *conn) Write(b []byte) (n int, err error) {
return 0, io.ErrClosedPipe
}
func (c *conn) LocalAddr() net.Addr {
return &net.TCPAddr{}
}
func (c *conn) RemoteAddr() net.Addr {
return &net.TCPAddr{}
}
func (c *conn) SetDeadline(t time.Time) error {
return nil
}
func (c *conn) SetReadDeadline(t time.Time) error {
return nil
}
func (c *conn) SetWriteDeadline(t time.Time) error {
return nil
}
+7 -2
View File
@@ -1,4 +1,4 @@
package forward
package direct
import (
"context"
@@ -16,6 +16,7 @@ func init() {
}
type directConnector struct {
md metadata
options connector.Options
}
@@ -31,7 +32,7 @@ func NewConnector(opts ...connector.Option) connector.Connector {
}
func (c *directConnector) Init(md md.Metadata) (err error) {
return nil
return c.parseMetadata(md)
}
func (c *directConnector) Connect(ctx context.Context, _ net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
@@ -40,6 +41,10 @@ func (c *directConnector) Connect(ctx context.Context, _ net.Conn, network, addr
opt(&cOpts)
}
if c.md.action == "reject" {
return &conn{}, nil
}
conn, err := cOpts.Dialer.Dial(ctx, network, address)
if err != nil {
return nil, err
+17
View File
@@ -0,0 +1,17 @@
package direct
import (
"strings"
mdata "github.com/go-gost/core/metadata"
mdutil "github.com/go-gost/x/metadata/util"
)
type metadata struct {
action string
}
func (c *directConnector) parseMetadata(md mdata.Metadata) (err error) {
c.md.action = strings.ToLower(mdutil.GetString(md, "action"))
return
}
+11 -7
View File
@@ -15,7 +15,8 @@ import (
"github.com/go-gost/core/connector"
"github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata"
ctxvalue "github.com/go-gost/x/ctx"
xctx "github.com/go-gost/x/ctx"
ictx "github.com/go-gost/x/internal/ctx"
"github.com/go-gost/x/registry"
)
@@ -49,12 +50,17 @@ func (c *http2Connector) Connect(ctx context.Context, conn net.Conn, network, ad
"remote": conn.RemoteAddr().String(),
"network": network,
"address": address,
"sid": string(ctxvalue.SidFromContext(ctx)),
"sid": string(xctx.SidFromContext(ctx)),
})
log.Debugf("connect %s/%s", address, network)
v, _ := conn.(md.Metadatable)
if v == nil {
var client *http.Client
if cc, ok := conn.(xctx.Context); ok {
if md := ictx.MetadataFromContext(cc.Context()); md != nil {
client, _ = md.Get("client").(*http.Client)
}
}
if client == nil {
err := errors.New("http2: wrong connection type")
log.Error(err)
return nil, err
@@ -69,7 +75,6 @@ func (c *http2Connector) Connect(ctx context.Context, conn net.Conn, network, ad
ProtoMinor: 0,
Header: c.md.header,
Body: pr,
// ContentLength: -1,
}
if req.Header == nil {
req.Header = make(http.Header)
@@ -92,8 +97,7 @@ func (c *http2Connector) Connect(ctx context.Context, conn net.Conn, network, ad
defer conn.SetDeadline(time.Time{})
}
client := v.Metadata().Get("client").(*http.Client)
resp, err := client.Do(req)
resp, err := client.Do(req.WithContext(context.WithoutCancel(ctx)))
if err != nil {
log.Error(err)
conn.Close()
+2 -2
View File
@@ -44,7 +44,7 @@ func (c *relayConnector) bindTCP(ctx context.Context, conn net.Conn, network, ad
if err != nil {
return nil, err
}
log.Debugf("bind on %s/%s OK", laddr, laddr.Network())
log.Infof("bind on %s/%s OK", laddr, laddr.Network())
session, err := mux.ServerSession(conn, c.md.muxCfg)
if err != nil {
@@ -63,7 +63,7 @@ func (c *relayConnector) bindUDP(ctx context.Context, conn net.Conn, network, ad
if err != nil {
return nil, err
}
log.Debugf("bind on %s/%s OK", laddr, laddr.Network())
log.Infof("bind on %s/%s OK", laddr, laddr.Network())
ln := udp.NewListener(
relay_util.UDPTunClientPacketConn(conn),
+7 -8
View File
@@ -192,14 +192,13 @@ func (c *bindUDPConn) Write(b []byte) (n int, err error) {
return
}
// 2-byte data length header
var bh [2]byte
binary.BigEndian.PutUint16(bh[:], uint16(len(b)))
_, err = c.Conn.Write(bh[:])
if err != nil {
return
}
return c.Conn.Write(b)
buf := bufpool.Get(len(b) + 2)
defer bufpool.Put(buf)
binary.BigEndian.PutUint16(buf[:2], uint16(len(b)))
n = copy(buf[2:], b)
return c.Conn.Write(buf)
}
func (c *bindUDPConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
+73
View File
@@ -0,0 +1,73 @@
package tunnel
import (
"encoding/binary"
"errors"
"fmt"
"io"
"math"
"net"
"github.com/go-gost/core/common/bufpool"
"github.com/go-gost/relay"
xrelay "github.com/go-gost/x/internal/util/relay"
)
type packetConn struct {
net.Conn
}
func (c *packetConn) Read(b []byte) (n int, err error) {
var bb [2]byte
_, err = io.ReadFull(c.Conn, bb[:])
if err != nil {
return
}
dlen := int(binary.BigEndian.Uint16(bb[:]))
if len(b) >= dlen {
return io.ReadFull(c.Conn, b[:dlen])
}
buf := bufpool.Get(dlen)
defer bufpool.Put(buf)
n, err = io.ReadFull(c.Conn, buf)
copy(b, buf[:n])
return
}
func (c *packetConn) Write(b []byte) (n int, err error) {
if len(b) > math.MaxUint16 {
err = errors.New("write: data maximum exceeded")
return
}
buf := bufpool.Get(len(b) + 2)
defer bufpool.Put(buf)
binary.BigEndian.PutUint16(buf[:2], uint16(len(b)))
n = copy(buf[2:], b)
return c.Conn.Write(buf)
}
func readResponse(r io.Reader) (err error) {
resp := relay.Response{}
_, err = resp.ReadFrom(r)
if err != nil {
return
}
if resp.Version != relay.Version1 {
err = relay.ErrBadVersion
return
}
if resp.Status != relay.StatusOK {
err = fmt.Errorf("%d %s", resp.Status, xrelay.StatusText(resp.Status))
return
}
return nil
}
+120
View File
@@ -0,0 +1,120 @@
package tunnel
import (
"context"
"fmt"
"net"
"time"
"github.com/go-gost/core/connector"
md "github.com/go-gost/core/metadata"
"github.com/go-gost/relay"
xctx "github.com/go-gost/x/ctx"
"github.com/go-gost/x/registry"
)
func init() {
registry.ConnectorRegistry().Register("router", NewConnector)
}
type routerConnector struct {
md metadata
options connector.Options
}
func NewConnector(opts ...connector.Option) connector.Connector {
options := connector.Options{}
for _, opt := range opts {
opt(&options)
}
return &routerConnector{
options: options,
}
}
func (c *routerConnector) Init(md md.Metadata) (err error) {
return c.parseMetadata(md)
}
func (c *routerConnector) Connect(ctx context.Context, conn net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
log := c.options.Logger.WithFields(map[string]any{
"network": network,
"address": address,
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"sid": xctx.SidFromContext(ctx).String(),
})
log.Debugf("connect %s/%s", address, network)
if c.md.connectTimeout > 0 {
conn.SetDeadline(time.Now().Add(c.md.connectTimeout))
defer conn.SetDeadline(time.Time{})
}
req := relay.Request{
Version: relay.Version1,
Cmd: relay.CmdAssociate,
}
if c.options.Auth != nil {
pwd, _ := c.options.Auth.Password()
req.Features = append(req.Features, &relay.UserAuthFeature{
Username: c.options.Auth.Username(),
Password: pwd,
})
}
switch network {
case "udp":
req.Features = append(req.Features, &relay.NetworkFeature{
Network: relay.NetworkUDP,
})
case "ip":
req.Features = append(req.Features, &relay.NetworkFeature{
Network: relay.NetworkIP,
})
}
srcAddr := conn.RemoteAddr().String()
if v := xctx.SrcAddrFromContext(ctx); v != nil {
srcAddr = v.String()
}
af := &relay.AddrFeature{}
af.ParseFrom(srcAddr)
req.Features = append(req.Features, af) // src address
af = &relay.AddrFeature{}
af.ParseFrom(address)
req.Features = append(req.Features, af) // dst address
if !c.md.routerID.IsZero() {
req.Features = append(req.Features, &relay.TunnelFeature{
ID: c.md.routerID,
})
}
if _, err := req.WriteTo(conn); err != nil {
return nil, err
}
// drain the response
if err := readResponse(conn); err != nil {
return nil, err
}
switch network {
case "udp", "ip":
conn = &packetConn{
Conn: conn,
}
default:
err := fmt.Errorf("network %s is unsupported", network)
log.Error(err)
return nil, err
}
log.Debugf("connect to router %s@%s OK", address, c.md.routerID)
return conn, nil
}
+34
View File
@@ -0,0 +1,34 @@
package tunnel
import (
"errors"
"time"
mdata "github.com/go-gost/core/metadata"
"github.com/go-gost/relay"
mdutil "github.com/go-gost/x/metadata/util"
"github.com/google/uuid"
)
var (
ErrInvalidRouterID = errors.New("router: invalid router ID")
)
type metadata struct {
connectTimeout time.Duration
routerID relay.TunnelID
}
func (c *routerConnector) parseMetadata(md mdata.Metadata) (err error) {
c.md.connectTimeout = mdutil.GetDuration(md, "connectTimeout")
if s := mdutil.GetString(md, "router.id"); s != "" {
uuid, err := uuid.Parse(s)
if err != nil {
return err
}
c.md.routerID = relay.NewTunnelID(uuid[:])
}
return
}
+1 -1
View File
@@ -4,8 +4,8 @@ import (
"time"
mdata "github.com/go-gost/core/metadata"
mdutil "github.com/go-gost/x/metadata/util"
"github.com/go-gost/x/internal/util/mux"
mdutil "github.com/go-gost/x/metadata/util"
)
const (
+1 -1
View File
@@ -85,7 +85,7 @@ func (c *ssuConnector) Connect(ctx context.Context, conn net.Conn, network, addr
}
// standard UDP relay
return ss.UDPClientConn(pc, conn.RemoteAddr(), taddr, c.md.bufferSize), nil
return ss.UDPClientConn(pc, conn.RemoteAddr(), taddr, c.md.udpBufferSize), nil
}
if c.cipher != nil {
+4 -16
View File
@@ -1,7 +1,6 @@
package ss
import (
"math"
"time"
mdata "github.com/go-gost/core/metadata"
@@ -11,24 +10,13 @@ import (
type metadata struct {
key string
connectTimeout time.Duration
bufferSize int
udpBufferSize int
}
func (c *ssuConnector) parseMetadata(md mdata.Metadata) (err error) {
const (
key = "key"
connectTimeout = "timeout"
bufferSize = "bufferSize" // udp buffer size
)
c.md.key = mdutil.GetString(md, key)
c.md.connectTimeout = mdutil.GetDuration(md, connectTimeout)
if bs := mdutil.GetInt(md, bufferSize); bs > 0 {
c.md.bufferSize = int(math.Min(math.Max(float64(bs), 512), 64*1024))
} else {
c.md.bufferSize = 4096
}
c.md.key = mdutil.GetString(md, "key")
c.md.connectTimeout = mdutil.GetDuration(md, "timeout", "connectTimeout")
c.md.udpBufferSize = mdutil.GetInt(md, "udpBufferSize", "udp.bufferSize")
return
}
+7 -8
View File
@@ -130,14 +130,13 @@ func (c *bindUDPConn) Write(b []byte) (n int, err error) {
return
}
// 2-byte data length header
var bh [2]byte
binary.BigEndian.PutUint16(bh[:], uint16(len(b)))
_, err = c.Conn.Write(bh[:])
if err != nil {
return
}
return c.Conn.Write(b)
buf := bufpool.Get(len(b) + 2)
defer bufpool.Put(buf)
binary.BigEndian.PutUint16(buf[:2], uint16(len(b)))
n = copy(buf[2:], b)
return c.Conn.Write(buf)
}
func (c *bindUDPConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
+7 -7
View File
@@ -9,7 +9,7 @@ import (
"github.com/go-gost/core/connector"
md "github.com/go-gost/core/metadata"
"github.com/go-gost/relay"
ctxvalue "github.com/go-gost/x/ctx"
xctx "github.com/go-gost/x/ctx"
"github.com/go-gost/x/registry"
)
@@ -39,11 +39,11 @@ func (c *tunnelConnector) Init(md md.Metadata) (err error) {
func (c *tunnelConnector) 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,
"sid": string(ctxvalue.SidFromContext(ctx)),
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"sid": xctx.SidFromContext(ctx).String(),
})
log.Debugf("connect %s/%s", address, network)
@@ -73,9 +73,9 @@ func (c *tunnelConnector) Connect(ctx context.Context, conn net.Conn, network, a
})
}
srcAddr := conn.LocalAddr().String()
if v := ctxvalue.ClientAddrFromContext(ctx); v != "" {
srcAddr = string(v)
srcAddr := conn.RemoteAddr().String()
if v := xctx.SrcAddrFromContext(ctx); v != nil {
srcAddr = v.String()
}
af := &relay.AddrFeature{}
+52 -71
View File
@@ -1,111 +1,92 @@
package ctx
import (
"bytes"
"context"
"github.com/go-gost/core/logger"
"net"
)
// clientAddrKey saves the client address.
type clientAddrKey struct{}
type ClientAddr string
var (
keyClientAddr clientAddrKey
)
func ContextWithClientAddr(ctx context.Context, addr ClientAddr) context.Context {
return context.WithValue(ctx, keyClientAddr, addr)
type Context interface {
Context() context.Context
}
func ClientAddrFromContext(ctx context.Context) ClientAddr {
v, _ := ctx.Value(keyClientAddr).(ClientAddr)
type WithContext interface {
WithContext(ctx context.Context)
}
type srcAddrKey struct{}
func ContextWithSrcAddr(ctx context.Context, addr net.Addr) context.Context {
return context.WithValue(ctx, srcAddrKey{}, addr)
}
func SrcAddrFromContext(ctx context.Context) net.Addr {
v, _ := ctx.Value(srcAddrKey{}).(net.Addr)
return v
}
// sidKey saves the session ID.
type sidKey struct{}
type Sid string
type dstAddrKey struct{}
var (
keySid sidKey
func ContextWithDstAddr(ctx context.Context, addr net.Addr) context.Context {
return context.WithValue(ctx, dstAddrKey{}, addr)
}
func DstAddrFromContext(ctx context.Context) net.Addr {
v, _ := ctx.Value(dstAddrKey{}).(net.Addr)
return v
}
type (
Sid string
// sidKey saves the session ID.
sidKey struct{}
)
func (s Sid) String() string {
return string(s)
}
func ContextWithSid(ctx context.Context, sid Sid) context.Context {
return context.WithValue(ctx, keySid, sid)
return context.WithValue(ctx, sidKey{}, sid)
}
func SidFromContext(ctx context.Context) Sid {
v, _ := ctx.Value(keySid).(Sid)
v, _ := ctx.Value(sidKey{}).(Sid)
return v
}
// hashKey saves the hash source for Selector.
type hashKey struct{}
type Hash struct {
Source string
}
var (
clientHashKey = &hashKey{}
type (
// hashKey saves the hash source for Selector.
hashKey struct{}
Hash struct {
Source string
}
)
func ContextWithHash(ctx context.Context, hash *Hash) context.Context {
return context.WithValue(ctx, clientHashKey, hash)
return context.WithValue(ctx, hashKey{}, hash)
}
func HashFromContext(ctx context.Context) *Hash {
if v, _ := ctx.Value(clientHashKey).(*Hash); v != nil {
if v, _ := ctx.Value(hashKey{}).(*Hash); v != nil {
return v
}
return nil
}
type clientIDKey struct{}
type ClientID string
var (
keyClientID = &clientIDKey{}
type (
ClientID string
clientIDKey struct{}
)
func (s ClientID) String() string {
return string(s)
}
func ContextWithClientID(ctx context.Context, clientID ClientID) context.Context {
return context.WithValue(ctx, keyClientID, clientID)
return context.WithValue(ctx, clientIDKey{}, clientID)
}
func ClientIDFromContext(ctx context.Context) ClientID {
v, _ := ctx.Value(keyClientID).(ClientID)
return v
}
type bufferKey struct{}
var (
keyBuffer = &bufferKey{}
)
func ContextWithBuffer(ctx context.Context, buffer *bytes.Buffer) context.Context {
return context.WithValue(ctx, keyBuffer, buffer)
}
func BufferFromContext(ctx context.Context) *bytes.Buffer {
v, _ := ctx.Value(keyBuffer).(*bytes.Buffer)
return v
}
type loggerKey struct{}
var (
keyLogger = &loggerKey{}
)
func ContextWithLogger(ctx context.Context, log logger.Logger) context.Context {
return context.WithValue(ctx, keyLogger, log)
}
func LoggerFromContext(ctx context.Context) logger.Logger {
v, _ := ctx.Value(keyLogger).(logger.Logger)
v, _ := ctx.Value(clientIDKey{}).(ClientID)
return v
}
+8
View File
@@ -8,6 +8,8 @@ import (
"github.com/go-gost/core/dialer"
"github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata"
xctx "github.com/go-gost/x/ctx"
"github.com/go-gost/x/internal/net/proxyproto"
xdtls "github.com/go-gost/x/internal/util/dtls"
"github.com/go-gost/x/registry"
"github.com/pion/dtls/v2"
@@ -50,6 +52,12 @@ func (d *dtlsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
return nil, err
}
conn = proxyproto.WrapClientConn(
d.options.ProxyProtocol,
xctx.SrcAddrFromContext(ctx),
xctx.DstAddrFromContext(ctx),
conn)
tlsCfg := d.options.TLSConfig
if tlsCfg == nil {
tlsCfg = &tls.Config{
+14 -1
View File
@@ -7,6 +7,8 @@ import (
"github.com/go-gost/core/dialer"
md "github.com/go-gost/core/metadata"
xctx "github.com/go-gost/x/ctx"
"github.com/go-gost/x/internal/net/proxyproto"
pb "github.com/go-gost/x/internal/util/grpc/proto"
"github.com/go-gost/x/registry"
"google.golang.org/grpc"
@@ -71,7 +73,18 @@ func (d *grpcDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
grpcOpts := []grpc.DialOption{
// grpc.WithBlock(),
grpc.WithContextDialer(func(c context.Context, s string) (net.Conn, error) {
return options.Dialer.Dial(c, "tcp", s)
conn, err := options.Dialer.Dial(c, "tcp", s)
if err != nil {
return nil, err
}
conn = proxyproto.WrapClientConn(
d.options.ProxyProtocol,
xctx.SrcAddrFromContext(ctx),
xctx.DstAddrFromContext(ctx),
conn)
return conn, nil
}),
grpc.WithAuthority(host),
grpc.WithConnectParams(grpc.ConnectParams{
+4 -6
View File
@@ -1,11 +1,10 @@
package http2
import (
"context"
"errors"
"net"
"time"
mdata "github.com/go-gost/core/metadata"
)
// a dummy HTTP2 client conn used by HTTP2 client connector
@@ -13,7 +12,7 @@ type conn struct {
localAddr net.Addr
remoteAddr net.Addr
onClose func()
md mdata.Metadata
ctx context.Context
}
func (c *conn) Close() error {
@@ -51,7 +50,6 @@ func (c *conn) SetWriteDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "nop", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
// Metadata implements metadata.Metadatable interface.
func (c *conn) Metadata() mdata.Metadata {
return c.md
func (c *conn) Context() context.Context {
return c.ctx
}
+18 -7
View File
@@ -10,7 +10,10 @@ import (
"github.com/go-gost/core/dialer"
"github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata"
xctx "github.com/go-gost/x/ctx"
ictx "github.com/go-gost/x/internal/ctx"
net_dialer "github.com/go-gost/x/internal/net/dialer"
"github.com/go-gost/x/internal/net/proxyproto"
mdx "github.com/go-gost/x/metadata"
"github.com/go-gost/x/registry"
)
@@ -23,7 +26,6 @@ type http2Dialer struct {
clients map[string]*http.Client
clientMutex sync.Mutex
logger logger.Logger
md metadata
options dialer.Options
}
@@ -91,7 +93,18 @@ func (d *http2Dialer) Dial(ctx context.Context, address string, opts ...dialer.D
if netd == nil {
netd = net_dialer.DefaultNetDialer
}
return netd.Dial(ctx, network, addr)
conn, err := netd.Dial(ctx, network, addr)
if err != nil {
return nil, err
}
conn = proxyproto.WrapClientConn(
d.options.ProxyProtocol,
xctx.SrcAddrFromContext(ctx),
xctx.DstAddrFromContext(ctx),
conn)
return conn, nil
},
ForceAttemptHTTP2: true,
MaxIdleConns: 16,
@@ -103,7 +116,7 @@ func (d *http2Dialer) Dial(ctx context.Context, address string, opts ...dialer.D
d.clients[address] = client
}
var c net.Conn = &conn{
return &conn{
localAddr: &net.TCPAddr{},
remoteAddr: raddr,
onClose: func() {
@@ -111,8 +124,6 @@ func (d *http2Dialer) Dial(ctx context.Context, address string, opts ...dialer.D
defer d.clientMutex.Unlock()
delete(d.clients, address)
},
md: mdx.NewMetadata(map[string]any{"client": client}),
}
return c, nil
ctx: ictx.ContextWithMetadata(ctx, mdx.NewMetadata(map[string]any{"client": client})),
}, nil
}
+27 -3
View File
@@ -15,6 +15,8 @@ import (
"github.com/go-gost/core/dialer"
"github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata"
xctx "github.com/go-gost/x/ctx"
"github.com/go-gost/x/internal/net/proxyproto"
"github.com/go-gost/x/registry"
"golang.org/x/net/http2"
)
@@ -94,14 +96,36 @@ func (d *h2Dialer) Dial(ctx context.Context, address string, opts ...dialer.Dial
client.Transport = &http2.Transport{
AllowHTTP: true,
DialTLSContext: func(ctx context.Context, network, addr string, cfg *tls.Config) (net.Conn, error) {
return options.Dialer.Dial(ctx, network, addr)
conn, err := options.Dialer.Dial(ctx, network, addr)
if err != nil {
return nil, err
}
conn = proxyproto.WrapClientConn(
d.options.ProxyProtocol,
xctx.SrcAddrFromContext(ctx),
xctx.DstAddrFromContext(ctx),
conn)
return conn, nil
},
}
} else {
client.Transport = &http.Transport{
TLSClientConfig: d.options.TLSConfig,
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return options.Dialer.Dial(ctx, network, addr)
conn, err := options.Dialer.Dial(ctx, network, addr)
if err != nil {
return nil, err
}
conn = proxyproto.WrapClientConn(
d.options.ProxyProtocol,
xctx.SrcAddrFromContext(ctx),
xctx.DstAddrFromContext(ctx),
conn)
return conn, nil
},
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
@@ -145,7 +169,7 @@ func (d *h2Dialer) Dial(ctx context.Context, address string, opts ...dialer.Dial
d.logger.Trace(string(dump))
}
resp, err := client.Do(req)
resp, err := client.Do(req.WithContext(context.WithoutCancel(ctx)))
if err != nil {
return nil, err
}
+3 -3
View File
@@ -70,9 +70,9 @@ func (d *http3Dialer) Dial(ctx context.Context, addr string, opts ...dialer.Dial
Host: host,
Client: &http.Client{
// Timeout: 60 * time.Second,
Transport: &http3.RoundTripper{
Transport: &http3.Transport{
TLSClientConfig: d.options.TLSConfig,
Dial: func(ctx context.Context, adr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
Dial: func(ctx context.Context, adr string, tlsCfg *tls.Config, cfg *quic.Config) (*quic.Conn, error) {
// d.options.Logger.Infof("dial: %s/%s, %s", addr, network, host)
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
@@ -90,7 +90,7 @@ func (d *http3Dialer) Dial(ctx context.Context, addr string, opts ...dialer.Dial
KeepAlivePeriod: d.md.keepAlivePeriod,
HandshakeIdleTimeout: d.md.handshakeTimeout,
MaxIdleTimeout: d.md.maxIdleTimeout,
Versions: []quic.VersionNumber{
Versions: []quic.Version{
quic.Version1,
},
MaxIncomingStreams: int64(d.md.maxStreams),
+1 -1
View File
@@ -74,7 +74,7 @@ func (d *wtDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOpt
header: d.md.header,
dialer: &wt.Dialer{
TLSClientConfig: d.options.TLSConfig,
DialAddr: func(ctx context.Context, adr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
DialAddr: func(ctx context.Context, adr string, tlsCfg *tls.Config, cfg *quic.Config) (*quic.Conn, error) {
// d.options.Logger.Infof("dial: %s, %s, %s", addr, adr, host)
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
+2 -2
View File
@@ -8,7 +8,7 @@ import (
)
type quicSession struct {
session quic.EarlyConnection
session *quic.Conn
}
func (session *quicSession) GetConn() (*quicConn, error) {
@@ -28,7 +28,7 @@ func (session *quicSession) Close() error {
}
type quicConn struct {
quic.Stream
*quic.Stream
laddr net.Addr
raddr net.Addr
}
+1 -1
View File
@@ -127,7 +127,7 @@ func (d *icmpDialer) initSession(ctx context.Context, addr net.Addr, conn net.Pa
KeepAlivePeriod: d.md.keepAlivePeriod,
HandshakeIdleTimeout: d.md.handshakeTimeout,
MaxIdleTimeout: d.md.maxIdleTimeout,
Versions: []quic.VersionNumber{
Versions: []quic.Version{
quic.Version1,
quic.Version2,
},
+8
View File
@@ -10,6 +10,8 @@ import (
"github.com/go-gost/core/dialer"
"github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata"
xctx "github.com/go-gost/x/ctx"
"github.com/go-gost/x/internal/net/proxyproto"
"github.com/go-gost/x/internal/util/mux"
"github.com/go-gost/x/registry"
)
@@ -72,6 +74,12 @@ func (d *mtcpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
return
}
conn = proxyproto.WrapClientConn(
d.options.ProxyProtocol,
xctx.SrcAddrFromContext(ctx),
xctx.DstAddrFromContext(ctx),
conn)
session = &muxSession{conn: conn}
d.sessions[addr] = session
}
+8
View File
@@ -11,6 +11,8 @@ import (
"github.com/go-gost/core/dialer"
"github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata"
xctx "github.com/go-gost/x/ctx"
"github.com/go-gost/x/internal/net/proxyproto"
"github.com/go-gost/x/internal/util/mux"
"github.com/go-gost/x/registry"
)
@@ -73,6 +75,12 @@ func (d *mtlsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
return
}
conn = proxyproto.WrapClientConn(
d.options.ProxyProtocol,
xctx.SrcAddrFromContext(ctx),
xctx.DstAddrFromContext(ctx),
conn)
session = &muxSession{conn: conn}
d.sessions[addr] = session
}
+8
View File
@@ -11,6 +11,8 @@ import (
"github.com/go-gost/core/dialer"
"github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata"
xctx "github.com/go-gost/x/ctx"
"github.com/go-gost/x/internal/net/proxyproto"
"github.com/go-gost/x/internal/util/mux"
ws_util "github.com/go-gost/x/internal/util/ws"
"github.com/go-gost/x/registry"
@@ -87,6 +89,12 @@ func (d *mwsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp
return
}
conn = proxyproto.WrapClientConn(
d.options.ProxyProtocol,
xctx.SrcAddrFromContext(ctx),
xctx.DstAddrFromContext(ctx),
conn)
session = &muxSession{conn: conn}
d.sessions[addr] = session
}
+2 -2
View File
@@ -8,7 +8,7 @@ import (
)
type quicSession struct {
session quic.EarlyConnection
session *quic.Conn
}
func (session *quicSession) GetConn() (*quicConn, error) {
@@ -28,7 +28,7 @@ func (session *quicSession) Close() error {
}
type quicConn struct {
quic.Stream
*quic.Stream
laddr net.Addr
raddr net.Addr
}
+2 -1
View File
@@ -106,11 +106,12 @@ func (d *quicDialer) initSession(ctx context.Context, addr net.Addr, conn net.Pa
KeepAlivePeriod: d.md.keepAlivePeriod,
HandshakeIdleTimeout: d.md.handshakeTimeout,
MaxIdleTimeout: d.md.maxIdleTimeout,
Versions: []quic.VersionNumber{
Versions: []quic.Version{
quic.Version1,
quic.Version2,
},
MaxIncomingStreams: int64(d.md.maxStreams),
EnableDatagrams: d.md.enableDatagram,
}
tlsCfg := d.options.TLSConfig
+2
View File
@@ -12,6 +12,7 @@ type metadata struct {
maxIdleTimeout time.Duration
handshakeTimeout time.Duration
maxStreams int
enableDatagram bool
cipherKey []byte
}
@@ -40,6 +41,7 @@ func (d *quicDialer) parseMetadata(md mdata.Metadata) (err error) {
d.md.handshakeTimeout = mdutil.GetDuration(md, handshakeTimeout)
d.md.maxIdleTimeout = mdutil.GetDuration(md, maxIdleTimeout)
d.md.maxStreams = mdutil.GetInt(md, maxStreams)
d.md.enableDatagram = mdutil.GetBool(md, "quic.enableDatagram", "enableDatagram")
return
}
+8
View File
@@ -8,6 +8,8 @@ import (
"github.com/go-gost/core/dialer"
md "github.com/go-gost/core/metadata"
xctx "github.com/go-gost/x/ctx"
"github.com/go-gost/x/internal/net/proxyproto"
ssh_util "github.com/go-gost/x/internal/util/ssh"
"github.com/go-gost/x/registry"
"golang.org/x/crypto/ssh"
@@ -73,6 +75,12 @@ func (d *sshDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp
defer conn.SetDeadline(time.Time{})
}
conn = proxyproto.WrapClientConn(
d.options.ProxyProtocol,
xctx.SrcAddrFromContext(ctx),
xctx.DstAddrFromContext(ctx),
conn)
session, err = d.initSession(ctx, addr, conn)
if err != nil {
conn.Close()
+9 -1
View File
@@ -3,12 +3,14 @@ package sshd
import (
"context"
"net"
"os"
"sync"
"time"
"os"
"github.com/go-gost/core/dialer"
md "github.com/go-gost/core/metadata"
xctx "github.com/go-gost/x/ctx"
"github.com/go-gost/x/internal/net/proxyproto"
ssh_util "github.com/go-gost/x/internal/util/ssh"
"github.com/go-gost/x/registry"
"golang.org/x/crypto/ssh"
@@ -76,6 +78,12 @@ func (d *sshdDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
defer conn.SetDeadline(time.Time{})
}
conn = proxyproto.WrapClientConn(
d.options.ProxyProtocol,
xctx.SrcAddrFromContext(ctx),
xctx.DstAddrFromContext(ctx),
conn)
session, err = d.initSession(ctx, addr, conn)
if err != nil {
conn.Close()
+16 -5
View File
@@ -7,6 +7,8 @@ import (
"github.com/go-gost/core/dialer"
"github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata"
xctx "github.com/go-gost/x/ctx"
"github.com/go-gost/x/internal/net/proxyproto"
"github.com/go-gost/x/registry"
)
@@ -15,18 +17,20 @@ func init() {
}
type tcpDialer struct {
md metadata
logger logger.Logger
md metadata
logger logger.Logger
options dialer.Options
}
func NewDialer(opts ...dialer.Option) dialer.Dialer {
options := &dialer.Options{}
options := dialer.Options{}
for _, opt := range opts {
opt(options)
opt(&options)
}
return &tcpDialer{
logger: options.Logger,
logger: options.Logger,
options: options,
}
}
@@ -44,5 +48,12 @@ func (d *tcpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp
if err != nil {
d.logger.Error(err)
}
conn = proxyproto.WrapClientConn(
d.options.ProxyProtocol,
xctx.SrcAddrFromContext(ctx),
xctx.DstAddrFromContext(ctx),
conn)
return conn, err
}
+12 -3
View File
@@ -9,6 +9,8 @@ import (
"github.com/go-gost/core/dialer"
"github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata"
xctx "github.com/go-gost/x/ctx"
"github.com/go-gost/x/internal/net/proxyproto"
"github.com/go-gost/x/registry"
)
@@ -18,7 +20,7 @@ func init() {
type tlsDialer struct {
md metadata
logger logger.Logger
log logger.Logger
options dialer.Options
}
@@ -29,7 +31,7 @@ func NewDialer(opts ...dialer.Option) dialer.Dialer {
}
return &tlsDialer{
logger: options.Logger,
log: options.Logger,
options: options,
}
}
@@ -46,8 +48,15 @@ func (d *tlsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp
conn, err := options.Dialer.Dial(ctx, "tcp", addr)
if err != nil {
d.logger.Error(err)
d.log.Error(err)
}
conn = proxyproto.WrapClientConn(
d.options.ProxyProtocol,
xctx.SrcAddrFromContext(ctx),
xctx.DstAddrFromContext(ctx),
conn)
return conn, err
}
+18 -5
View File
@@ -7,6 +7,8 @@ import (
"github.com/go-gost/core/dialer"
"github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata"
xctx "github.com/go-gost/x/ctx"
"github.com/go-gost/x/internal/net/proxyproto"
"github.com/go-gost/x/registry"
)
@@ -15,8 +17,9 @@ func init() {
}
type udpDialer struct {
md metadata
logger logger.Logger
md metadata
logger logger.Logger
options dialer.Options
}
func NewDialer(opts ...dialer.Option) dialer.Dialer {
@@ -26,7 +29,8 @@ func NewDialer(opts ...dialer.Option) dialer.Dialer {
}
return &udpDialer{
logger: options.Logger,
logger: options.Logger,
options: *options,
}
}
@@ -44,7 +48,16 @@ func (d *udpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp
if err != nil {
return nil, err
}
return &conn{
c = &conn{
UDPConn: c.(*net.UDPConn),
}, nil
}
c = proxyproto.WrapClientConn(
d.options.ProxyProtocol,
xctx.SrcAddrFromContext(ctx),
xctx.DstAddrFromContext(ctx),
c)
return c, nil
}
+9
View File
@@ -8,6 +8,8 @@ import (
"github.com/go-gost/core/dialer"
md "github.com/go-gost/core/metadata"
xctx "github.com/go-gost/x/ctx"
"github.com/go-gost/x/internal/net/proxyproto"
ws_util "github.com/go-gost/x/internal/util/ws"
"github.com/go-gost/x/registry"
"github.com/gorilla/websocket"
@@ -61,6 +63,13 @@ func (d *wsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOpt
if err != nil {
d.options.Logger.Error(err)
}
conn = proxyproto.WrapClientConn(
d.options.ProxyProtocol,
xctx.SrcAddrFromContext(ctx),
xctx.DstAddrFromContext(ctx),
conn)
return conn, err
}
+55 -45
View File
@@ -1,80 +1,88 @@
module github.com/go-gost/x
go 1.22
go 1.24
toolchain go1.22.2
toolchain go1.24.5
require (
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
github.com/gin-contrib/cors v1.6.0
github.com/gin-gonic/gin v1.9.1
github.com/go-gost/core v0.1.7
github.com/gin-contrib/cors v1.7.2
github.com/gin-gonic/gin v1.10.1
github.com/go-gost/core v0.3.3
github.com/go-gost/gosocks4 v0.0.1
github.com/go-gost/gosocks5 v0.4.2
github.com/go-gost/plugin v0.1.1
github.com/go-gost/plugin v0.2.1
github.com/go-gost/relay v0.5.0
github.com/go-gost/tls-dissector v0.1.1
github.com/go-redis/redis/v8 v8.11.5
github.com/gobwas/glob v0.2.3
github.com/golang/snappy v0.0.4
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.1
github.com/gorilla/websocket v1.5.3
github.com/miekg/dns v1.1.61
github.com/mitchellh/go-homedir v1.1.0
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pion/dtls/v2 v2.2.6
github.com/pires/go-proxyproto v0.7.0
github.com/pires/go-proxyproto v0.8.1
github.com/prometheus/client_golang v1.19.1
github.com/quic-go/quic-go v0.45.0
github.com/quic-go/webtransport-go v0.8.0
github.com/quic-go/quic-go v0.53.0
github.com/quic-go/webtransport-go v0.9.0
github.com/rs/xid v1.3.0
github.com/shadowsocks/go-shadowsocks2 v0.1.5
github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601
github.com/sirupsen/logrus v1.8.1
github.com/shadowsocks/go-shadowsocks2 v0.1.6-0.20241020092332-e1fe9ea73740
github.com/sirupsen/logrus v1.9.3
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
github.com/spf13/viper v1.18.2
github.com/vishvananda/netlink v1.1.0
github.com/spf13/viper v1.19.0
github.com/stretchr/testify v1.9.0
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54
github.com/vishvananda/netns v0.0.4
github.com/vulcand/predicate v1.2.0
github.com/xjasonlyu/tun2socks/v2 v2.6.0
github.com/xtaci/kcp-go/v5 v5.6.5
github.com/xtaci/smux v1.5.24
github.com/xtaci/smux v1.5.31
github.com/xtaci/tcpraw v1.2.25
github.com/yl2chen/cidranger v1.0.2
github.com/zalando/go-keyring v0.2.4
golang.org/x/crypto v0.25.0
golang.org/x/net v0.27.0
golang.org/x/sys v0.22.0
golang.org/x/time v0.5.0
golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478
google.golang.org/grpc v1.65.0
google.golang.org/protobuf v1.34.2
golang.org/x/crypto v0.40.0
golang.org/x/exp v0.0.0-20241210194714-1829a127f884
golang.org/x/net v0.42.0
golang.org/x/sys v0.34.0
golang.org/x/text v0.27.0
golang.org/x/time v0.11.0
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb
google.golang.org/grpc v1.67.1
google.golang.org/protobuf v1.35.1
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1
gvisor.dev/gvisor v0.0.0-20250523182742-eede7a881b20
)
require (
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
github.com/alessio/shellescape v1.4.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bytedance/sonic v1.11.2 // indirect
github.com/bytedance/sonic v1.11.6 // indirect
github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/coreos/go-iptables v0.5.0 // indirect
github.com/danieljoos/wincred v1.2.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.19.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba // indirect
github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jonboulle/clockwork v0.2.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/klauspost/reedsolomon v1.11.8 // indirect
@@ -84,16 +92,17 @@ require (
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/onsi/ginkgo/v2 v2.19.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/onsi/gomega v1.34.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pion/logging v0.2.2 // indirect
github.com/pion/transport/v2 v2.0.2 // indirect
github.com/pion/udp/v2 v2.0.1 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.6.0 // indirect
github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
@@ -102,20 +111,21 @@ require (
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/templexxx/cpu v0.1.0 // indirect
github.com/templexxx/xorsimd v0.4.2 // indirect
github.com/templexxx/cpu v0.1.1 // indirect
github.com/templexxx/xorsimd v0.4.3 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
go.uber.org/mock v0.4.0 // indirect
go.uber.org/mock v0.5.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.7.0 // indirect
golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 // indirect
golang.org/x/mod v0.19.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/tools v0.23.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/mod v0.25.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/term v0.33.0 // indirect
golang.org/x/tools v0.34.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
)
replace github.com/ugorji/go v1.1.4 => github.com/ugorji/go/codec v1.2.12
+110 -103
View File
@@ -1,7 +1,5 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0=
@@ -10,21 +8,18 @@ github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
github.com/bytedance/sonic v1.11.2 h1:ywfwo0a/3j9HR8wsYGWsIWl2mvRsI950HyoxiBERw5A=
github.com/bytedance/sonic v1.11.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0=
github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coreos/go-iptables v0.5.0 h1:mw6SAibtHKZcNzAsOxjoHIG0gy5YFHhypWSSNc6EjbQ=
github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
@@ -37,6 +32,7 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
@@ -47,38 +43,34 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/cors v1.6.0 h1:0Z7D/bVhE6ja07lI8CTjTonp6SB07o8bNuFyRbsBUQg=
github.com/gin-contrib/cors v1.6.0/go.mod h1:cI+h6iOAyxKRtUtC6iF/Si1KSFvGm/gK+kshxlCi8ro=
github.com/gin-contrib/cors v1.7.2 h1:oLDHxdg8W/XDoN/8zamqk/Drgt4oVZDvaV0YmvVICQw=
github.com/gin-contrib/cors v1.7.2/go.mod h1:SUJVARKgQ40dmrzgXEVxj2m7Ig1v1qIboQkPDTQ9t2E=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-gost/core v0.1.7 h1:EP2Ux2SZR1omV6362MfTZG+oZTuaAODGqc0M9c/ZdEI=
github.com/go-gost/core v0.1.7/go.mod h1:WGI43jOka7FAsSAwi/fSMaqxdR+E339ycb4NBGlFr6A=
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-gost/core v0.3.3 h1:YN15FQptQoNB0MlMR283C99w2EeGql9Cm+4QVlN6zs0=
github.com/go-gost/core v0.3.3/go.mod h1:WGI43jOka7FAsSAwi/fSMaqxdR+E339ycb4NBGlFr6A=
github.com/go-gost/gosocks4 v0.0.1 h1:+k1sec8HlELuQV7rWftIkmy8UijzUt2I6t+iMPlGB2s=
github.com/go-gost/gosocks4 v0.0.1/go.mod h1:3B6L47HbU/qugDg4JnoFPHgJXE43Inz8Bah1QaN9qCc=
github.com/go-gost/gosocks5 v0.4.2 h1:IianxHTkACPqCwiOAT3MHoMdSUl+SEPSRu1ikawC1Pc=
github.com/go-gost/gosocks5 v0.4.2/go.mod h1:1G6I7HP7VFVxveGkoK8mnprnJqSqJjdcASKsdUn4Pp4=
github.com/go-gost/plugin v0.1.1 h1:LNoc/Rqwb3ceGhhxwhpjf1SeeYBGe80MJ9E4lpM3qak=
github.com/go-gost/plugin v0.1.1/go.mod h1:oN23l+yGDCIP9G3KnDl/I/0zVGOobZUDCB2Z5yYYXts=
github.com/go-gost/plugin v0.2.1 h1:zkkgCZd+/X+jmaDElP7z2fZVTFv+Pjq2DLmnnNWPMFA=
github.com/go-gost/plugin v0.2.1/go.mod h1:oN23l+yGDCIP9G3KnDl/I/0zVGOobZUDCB2Z5yYYXts=
github.com/go-gost/relay v0.5.0 h1:JG1tgy/KWiVXS0ukuVXvbM0kbYuJTWxYpJ5JwzsCf/c=
github.com/go-gost/relay v0.5.0/go.mod h1:lcX+23LCQ3khIeASBo+tJ/WbwXFO32/N5YN6ucuYTG8=
github.com/go-gost/tls-dissector v0.1.1 h1:2zUOTPzCQAUQ54Rpy0UEi3JPMQSYsIFSeFeKrzmkCoU=
github.com/go-gost/tls-dissector v0.1.1/go.mod h1:/9QfdewqmHdaE362Hv5nDaSWLx3pCmtD870d6GaquXs=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4=
github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
@@ -98,6 +90,8 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -107,14 +101,18 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba h1:ql1qNgCyOB7iAEk8JTNM+zJrgIbnyCKX/wdlyPufP5g=
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf h1:C1GPyPJrOlJlIrcaBBiBpDsqZena2Ks8spa5xZqr1XQ=
github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf/go.mod h1:zXqxTI6jXDdKnlf8s+nT+3c8LrwUEy3yNpO4XJL90lA=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
@@ -148,14 +146,12 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pion/dtls/v2 v2.2.6 h1:yXMxKr0Skd+Ub6A8UqXTRLSywskx93ooMRHsQUtd+Z4=
github.com/pion/dtls/v2 v2.2.6/go.mod h1:t8fWJCIquY5rlQZwA2yWxUS1+OCrAdXrhVKXB5oD/wY=
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
@@ -164,8 +160,8 @@ github.com/pion/transport/v2 v2.0.2 h1:St+8o+1PEzPT51O9bv+tH/KYYLMNR5Vwm5Z3Qkjsy
github.com/pion/transport/v2 v2.0.2/go.mod h1:vrz6bUbFr/cjdwbnxq8OdDDzHf7JJfGsIRkxfpZoTA0=
github.com/pion/udp/v2 v2.0.1 h1:xP0z6WNux1zWEjhC7onRA3EwwSliXqu1ElUZAQhUP54=
github.com/pion/udp/v2 v2.0.1/go.mod h1:B7uvTMP00lzWdyMr/1PVZXtV3wpPIxBRd4Wl6AksXn8=
github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwyq0Hs=
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0=
github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -174,18 +170,18 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos=
github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8=
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/quic-go v0.45.0 h1:OHmkQGM37luZITyTSu6ff03HP/2IrwDX1ZFiNEhSFUE=
github.com/quic-go/quic-go v0.45.0/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI=
github.com/quic-go/webtransport-go v0.8.0 h1:HxSrwun11U+LlmwpgM1kEqIqH90IT4N8auv/cD7QFJg=
github.com/quic-go/webtransport-go v0.8.0/go.mod h1:N99tjprW432Ut5ONql/aUhSLT0YVSlwHohQsuac9WaM=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.53.0 h1:QHX46sISpG2S03dPeZBgVIZp8dGagIaiu2FiVYvpCZI=
github.com/quic-go/quic-go v0.53.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY=
github.com/quic-go/webtransport-go v0.9.0 h1:jgys+7/wm6JarGDrW+lD/r9BGqBAmqY/ssklE09bA70=
github.com/quic-go/webtransport-go v0.9.0/go.mod h1:4FUYIiUc75XSsF6HShcLeXXYZJ9AGwo/xh3L8M/P1ao=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
@@ -196,12 +192,11 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/shadowsocks/go-shadowsocks2 v0.1.5 h1:PDSQv9y2S85Fl7VBeOMF9StzeXZyK1HakRm86CUbr28=
github.com/shadowsocks/go-shadowsocks2 v0.1.5/go.mod h1:AGGpIoek4HRno4xzyFiAtLHkOpcoznZEkAccaI/rplM=
github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601 h1:XU9hik0exChEmY92ALW4l9WnDodxLVS9yOSNh2SizaQ=
github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601/go.mod h1:mttDPaeLm87u74HMrP+n2tugXvIKWcwff/cqSX0lehY=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/shadowsocks/go-shadowsocks2 v0.1.6-0.20241020092332-e1fe9ea73740 h1:XdDrN8rtxdgW3TLn7pAuobI9PhPMbf6Geu9nvFzXn2E=
github.com/shadowsocks/go-shadowsocks2 v0.1.6-0.20241020092332-e1fe9ea73740/go.mod h1:Oqfn/ykzqjeX00+7IuPyR7wGYgOzld0Tni6djgElacI=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8=
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWKbyjl083/loAegFkfbFNx5i2qEP4CNbm7E=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
@@ -212,44 +207,51 @@ github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
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.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/templexxx/cpu v0.1.0 h1:wVM+WIJP2nYaxVxqgHPD4wGA2aJ9rvrQRV8CvFzNb40=
github.com/templexxx/cpu v0.1.0/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
github.com/templexxx/xorsimd v0.4.2 h1:ocZZ+Nvu65LGHmCLZ7OoCtg8Fx8jnHKK37SjvngUoVI=
github.com/templexxx/xorsimd v0.4.2/go.mod h1:HgwaPoDREdi6OnULpSfxhzaiiSUY4Fi3JPn1wpt28NI=
github.com/templexxx/cpu v0.1.1 h1:isxHaxBXpYFWnk2DReuKkigaZyrjs2+9ypIdGP4h+HI=
github.com/templexxx/cpu v0.1.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
github.com/templexxx/xorsimd v0.4.3 h1:9AQTFHd7Bhk3dIT7Al2XeBX5DWOvsUPZCuhyAtNbHjU=
github.com/templexxx/xorsimd v0.4.3/go.mod h1:oZQcD6RFDisW2Am58dSAGwwL6rHjbzrlu25VDqfWkQg=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54 h1:8mhqcHPqTMhSPoslhGYihEgSfc77+7La1P6kiB6+9So=
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/vulcand/predicate v1.2.0 h1:uFsW1gcnnR7R+QTID+FVcs0sSYlIGntoGOTb3rQJt50=
github.com/vulcand/predicate v1.2.0/go.mod h1:VipoNYXny6c8N381zGUWkjuuNHiRbeAZhE7Qm9c+2GA=
github.com/xjasonlyu/tun2socks/v2 v2.6.0 h1:gI9saJT3XgH4e6v9jBuHRLwK7l3aN9YFWec/SsDTDx4=
github.com/xjasonlyu/tun2socks/v2 v2.6.0/go.mod h1:35AwqxIxnMkfBfT0UJ1Lku7PZm2ZiZJ8sxHyp0gt1yw=
github.com/xtaci/kcp-go/v5 v5.6.5 h1:oxGZNobj3OddrLzwdJYnR/waNgwrL98u02u0DWNHE3k=
github.com/xtaci/kcp-go/v5 v5.6.5/go.mod h1:Qy3Zf2tWTdFdEs0E8JvhrX+39r5UDZoYac8anvud7/Q=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
github.com/xtaci/smux v1.5.24 h1:77emW9dtnOxxOQ5ltR+8BbsX1kzcOxQ5gB+aaV9hXOY=
github.com/xtaci/smux v1.5.24/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
github.com/xtaci/smux v1.5.31 h1:3ha7sHtH46h85Iv7MfQogxasuRt1KPRhoFB3S4rmHgU=
github.com/xtaci/smux v1.5.31/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
github.com/xtaci/tcpraw v1.2.25 h1:VDlqo0op17JeXBM6e2G9ocCNLOJcw9mZbobMbJjo0vk=
github.com/xtaci/tcpraw v1.2.25/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1GgJQbLk=
github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU=
@@ -257,33 +259,33 @@ github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zalando/go-keyring v0.2.4 h1:wi2xxTqdiwMKbM6TWwi+uJCG/Tum2UV0jqaQhCa9/68=
github.com/zalando/go-keyring v0.2.4/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 h1:LoYXNGAShUG3m/ehNk4iFctuhGX/+R1ZpfJ4/ia80JM=
golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU=
golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -291,50 +293,52 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -343,35 +347,36 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 h1:Ug9qvr1myri/zFN6xL17LSCBGFDnphBBhzmILHsM5TY=
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478 h1:vDy//hdR+GnROE3OdYbQKt9rdtNdHkDtONvpRwmls/0=
golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478/go.mod h1:bVQfyl2sCM/QIIGHpWbFGfHPuDvqnCNkT6MQLTCjO/U=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2 h1:B82qJJgjvYKsXS9jeunTOisW56dUokqW/FOteYJJ/yg=
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb h1:whnFRlWMcXI9d+ZbWg+4sHnLp52d5yiIPUxMBSt4X9A=
golang.zx2c4.com/wireguard v0.0.0-20250521234502-f333402bd9cb/go.mod h1:rpwXGsirqLqN2L0JDJQlwOboGHmptD5ZD6T2VmcqhTw=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc=
google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ=
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -385,6 +390,8 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gvisor.dev/gvisor v0.0.0-20250523182742-eede7a881b20 h1:0DxLu8hxI1OGp1qVRPqNd+2k1a7hMNUNqbZG0IrtKlM=
gvisor.dev/gvisor v0.0.0-20250523182742-eede7a881b20/go.mod h1:3r5CMtNQMKIvBlrmM9xWUNamjKBYPOWyXOjmg5Kts3g=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
+90
View File
@@ -0,0 +1,90 @@
package api
import (
"context"
"net"
"net/http"
"github.com/gin-gonic/gin"
"github.com/go-gost/core/handler"
md "github.com/go-gost/core/metadata"
"github.com/go-gost/x/api"
"github.com/go-gost/x/registry"
)
func init() {
registry.HandlerRegistry().Register("api", NewHandler)
}
type apiHandler struct {
handler http.Handler
md metadata
options handler.Options
}
func NewHandler(opts ...handler.Option) handler.Handler {
options := handler.Options{}
for _, opt := range opts {
opt(&options)
}
return &apiHandler{
options: options,
}
}
func (h *apiHandler) Init(md md.Metadata) (err error) {
if err = h.parseMetadata(md); err != nil {
return
}
gin.SetMode(gin.ReleaseMode)
r := gin.New()
api.Register(r, &api.Options{
AccessLog: h.md.accesslog,
PathPrefix: h.md.pathPrefix,
Auther: h.options.Auther,
})
h.handler = r
return
}
func (h *apiHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
l := &singleConnListener{
conn: make(chan net.Conn, 1),
}
l.send(conn)
s := http.Server{
Handler: h.handler,
}
s.Serve(l)
return s.Shutdown(ctx)
}
type singleConnListener struct {
conn chan net.Conn
}
func (l *singleConnListener) Accept() (net.Conn, error) {
if conn, ok := <-l.conn; ok {
return conn, nil
}
return nil, net.ErrClosed
}
func (l *singleConnListener) Close() error {
return nil
}
func (l *singleConnListener) Addr() net.Addr {
return &net.TCPAddr{}
}
func (l *singleConnListener) send(conn net.Conn) {
l.conn <- conn
close(l.conn)
}
+17
View File
@@ -0,0 +1,17 @@
package api
import (
mdata "github.com/go-gost/core/metadata"
mdutil "github.com/go-gost/x/metadata/util"
)
type metadata struct {
accesslog bool
pathPrefix string
}
func (h *apiHandler) parseMetadata(md mdata.Metadata) (err error) {
h.md.accesslog = mdutil.GetBool(md, "api.accessLog", "accessLog")
h.md.pathPrefix = mdutil.GetString(md, "api.pathPrefix", "pathPrefix")
return
}
+16 -15
View File
@@ -9,6 +9,7 @@ import (
"strings"
"time"
"github.com/go-gost/core/bypass"
"github.com/go-gost/core/chain"
"github.com/go-gost/core/common/bufpool"
"github.com/go-gost/core/handler"
@@ -18,10 +19,12 @@ import (
md "github.com/go-gost/core/metadata"
"github.com/go-gost/core/observer/stats"
"github.com/go-gost/core/recorder"
ctxvalue "github.com/go-gost/x/ctx"
xctx "github.com/go-gost/x/ctx"
xhop "github.com/go-gost/x/hop"
ictx "github.com/go-gost/x/internal/ctx"
resolver_util "github.com/go-gost/x/internal/util/resolver"
rate_limiter "github.com/go-gost/x/limiter/rate"
xstats "github.com/go-gost/x/observer/stats"
stats_wrapper "github.com/go-gost/x/observer/stats/wrapper"
xrecorder "github.com/go-gost/x/recorder"
"github.com/go-gost/x/registry"
@@ -141,29 +144,27 @@ func (h *dnsHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.
Network: conn.LocalAddr().Network(),
RemoteAddr: conn.RemoteAddr().String(),
LocalAddr: conn.LocalAddr().String(),
Host: conn.LocalAddr().String(),
Proto: "dns",
Time: start,
SID: string(ctxvalue.SidFromContext(ctx)),
SID: xctx.SidFromContext(ctx).String(),
}
ro.ClientIP = conn.RemoteAddr().String()
if clientAddr := ctxvalue.ClientAddrFromContext(ctx); clientAddr != "" {
ro.ClientIP = string(clientAddr)
}
if h, _, _ := net.SplitHostPort(ro.ClientIP); h != "" {
ro.ClientIP = h
if srcAddr := xctx.SrcAddrFromContext(ctx); srcAddr != nil {
ro.ClientAddr = srcAddr.String()
}
log := h.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"sid": ctxvalue.SidFromContext(ctx),
"client": ro.ClientIP,
"network": ro.Network,
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"client": ro.ClientAddr,
"sid": ro.SID,
})
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
pStats := stats.Stats{}
pStats := xstats.Stats{}
conn = stats_wrapper.WrapConn(conn, &pStats)
defer func() {
@@ -260,7 +261,7 @@ func (h *dnsHandler) request(ctx context.Context, msg []byte, ro *xrecorder.Hand
}()
if h.options.Bypass != nil && mq.Question[0].Qclass == dns.ClassINET {
if h.options.Bypass.Contains(context.Background(), "udp", strings.Trim(mq.Question[0].Name, ".")) {
if h.options.Bypass.Contains(context.Background(), "udp", strings.Trim(mq.Question[0].Name, "."), bypass.WithService(h.options.Service)) {
log.Debug("bypass: ", mq.Question[0].Name)
mr = (&dns.Msg{}).SetReply(&mq)
b := bufpool.Get(h.md.bufferSize)
@@ -312,7 +313,7 @@ func (h *dnsHandler) request(ctx context.Context, msg []byte, ro *xrecorder.Hand
log.Debugf("exchange message %d: %s", mq.Id, mq.Question[0].String())
var buf bytes.Buffer
mr, err := h.exchange(ctxvalue.ContextWithBuffer(ctx, &buf), ex, &mq)
mr, err := h.exchange(ictx.ContextWithBuffer(ctx, &buf), ex, &mq)
ro.Route = buf.String()
if err != nil {
return nil, err
+17 -3
View File
@@ -4,12 +4,16 @@ import (
"context"
"net"
"net/http"
"net/http/httputil"
"sync"
"time"
"github.com/go-gost/core/auth"
"github.com/go-gost/core/handler"
"github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata"
"github.com/go-gost/core/recorder"
xctx "github.com/go-gost/x/ctx"
xrecorder "github.com/go-gost/x/recorder"
"github.com/go-gost/x/registry"
)
@@ -65,7 +69,13 @@ func (h *fileHandler) Init(md md.Metadata) (err error) {
}
func (h *fileHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
var clientAddr string
if srcAddr := xctx.SrcAddrFromContext(ctx); srcAddr != nil {
clientAddr = srcAddr.String()
}
h.options.Logger.WithFields(map[string]any{
"client": clientAddr,
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
}).Infof("%s - %s", conn.RemoteAddr(), conn.LocalAddr())
@@ -101,12 +111,16 @@ func (h *fileHandler) handleFunc(w http.ResponseWriter, r *http.Request) {
},
Time: start,
}
ro.ClientIP, _, _ = net.SplitHostPort(r.RemoteAddr)
log := h.options.Logger.WithFields(map[string]any{
"remote": r.RemoteAddr,
})
if log.IsLevelEnabled(logger.TraceLevel) {
dump, _ := httputil.DumpRequest(r, false)
log.Trace(string(dump))
}
rw := &responseWriter{ResponseWriter: w, statusCode: http.StatusOK}
defer func() {
@@ -122,13 +136,13 @@ func (h *fileHandler) handleFunc(w http.ResponseWriter, r *http.Request) {
log.WithFields(map[string]any{
"duration": time.Since(start),
}).Infof("%s %s %d %d", r.Method, r.RequestURI, rw.statusCode, rw.contentLength)
}).Infof("%s %s %s %d %d", r.Method, r.RequestURI, r.Proto, rw.statusCode, rw.contentLength)
}()
if auther := h.options.Auther; auther != nil {
u, p, _ := r.BasicAuth()
ro.ClientID = u
if _, ok := auther.Authenticate(r.Context(), u, p); !ok {
if _, ok := auther.Authenticate(r.Context(), u, p, auth.WithService(ro.Service)); !ok {
w.Header().Set("WWW-Authenticate", "Basic")
w.WriteHeader(http.StatusUnauthorized)
return
+44 -24
View File
@@ -5,7 +5,6 @@ import (
"bytes"
"context"
"errors"
"fmt"
"net"
"time"
@@ -15,12 +14,15 @@ import (
md "github.com/go-gost/core/metadata"
"github.com/go-gost/core/observer/stats"
"github.com/go-gost/core/recorder"
ctxvalue "github.com/go-gost/x/ctx"
xctx "github.com/go-gost/x/ctx"
ictx "github.com/go-gost/x/internal/ctx"
xnet "github.com/go-gost/x/internal/net"
"github.com/go-gost/x/internal/net/proxyproto"
"github.com/go-gost/x/internal/util/forwarder"
"github.com/go-gost/x/internal/util/sniffing"
tls_util "github.com/go-gost/x/internal/util/tls"
rate_limiter "github.com/go-gost/x/limiter/rate"
xstats "github.com/go-gost/x/observer/stats"
stats_wrapper "github.com/go-gost/x/observer/stats/wrapper"
xrecorder "github.com/go-gost/x/recorder"
"github.com/go-gost/x/registry"
@@ -86,26 +88,29 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
LocalAddr: conn.LocalAddr().String(),
Network: "tcp",
Time: start,
SID: string(ctxvalue.SidFromContext(ctx)),
SID: xctx.SidFromContext(ctx).String(),
}
ro.ClientIP = conn.RemoteAddr().String()
if clientAddr := ctxvalue.ClientAddrFromContext(ctx); clientAddr != "" {
ro.ClientIP = string(clientAddr)
if srcAddr := xctx.SrcAddrFromContext(ctx); srcAddr != nil {
ro.ClientAddr = srcAddr.String()
}
if h, _, _ := net.SplitHostPort(ro.ClientIP); h != "" {
ro.ClientIP = h
network := "tcp"
if _, ok := conn.(net.PacketConn); ok {
network = "udp"
}
ro.Network = network
log := h.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"sid": ctxvalue.SidFromContext(ctx),
"client": ro.ClientIP,
"network": ro.Network,
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"client": ro.ClientAddr,
"sid": ro.SID,
})
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
pStats := stats.Stats{}
pStats := xstats.Stats{}
conn = stats_wrapper.WrapConn(conn, &pStats)
defer func() {
@@ -130,12 +135,6 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
return rate_limiter.ErrRateLimit
}
network := "tcp"
if _, ok := conn.(net.PacketConn); ok {
network = "udp"
}
ro.Network = network
var proto string
if network == "tcp" && h.md.sniffing {
if h.md.sniffingTimeout > 0 {
@@ -152,8 +151,15 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
dial := func(ctx context.Context, network, address string) (net.Conn, error) {
var buf bytes.Buffer
cc, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), "tcp", address)
cc, err := h.options.Router.Dial(ictx.ContextWithBuffer(ctx, &buf), "tcp", address)
ro.Route = buf.String()
cc = proxyproto.WrapClientConn(
h.md.proxyProtocol,
xctx.SrcAddrFromContext(ctx),
xctx.DstAddrFromContext(ctx),
cc)
return cc, err
}
sniffer := &forwarder.Sniffer{
@@ -173,6 +179,7 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
switch proto {
case sniffing.ProtoHTTP:
return sniffer.HandleHTTP(ctx, conn,
forwarder.WithService(h.options.Service),
forwarder.WithDial(dial),
forwarder.WithHop(h.hop),
forwarder.WithBypass(h.options.Bypass),
@@ -182,6 +189,7 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
)
case sniffing.ProtoTLS:
return sniffer.HandleTLS(ctx, conn,
forwarder.WithService(h.options.Service),
forwarder.WithDial(dial),
forwarder.WithHop(h.hop),
forwarder.WithBypass(h.options.Bypass),
@@ -219,14 +227,15 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
ro.Host = addr
log = log.WithFields(map[string]any{
"node": target.Name,
"dst": fmt.Sprintf("%s/%s", addr, network),
"node": target.Name,
"dst": addr,
"network": network,
})
log.Debugf("%s >> %s", conn.RemoteAddr(), addr)
var buf bytes.Buffer
cc, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), network, addr)
cc, err := h.options.Router.Dial(ictx.ContextWithBuffer(ctx, &buf), network, addr)
ro.Route = buf.String()
if err != nil {
log.Error(err)
@@ -242,9 +251,20 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
}
defer cc.Close()
cc = proxyproto.WrapClientConn(
h.md.proxyProtocol,
xctx.SrcAddrFromContext(ctx),
xctx.DstAddrFromContext(ctx),
cc)
log = log.WithFields(map[string]any{"src": cc.LocalAddr().String(), "dst": cc.RemoteAddr().String()})
ro.SrcAddr = cc.LocalAddr().String()
ro.DstAddr = cc.RemoteAddr().String()
t := time.Now()
log.Infof("%s <-> %s", conn.RemoteAddr(), target.Addr)
xnet.Transport(conn, cc)
// xnet.Transport(conn, cc)
xnet.Pipe(ctx, conn, cc)
log.WithFields(map[string]any{
"duration": time.Since(t),
}).Infof("%s >-< %s", conn.RemoteAddr(), target.Addr)
+2
View File
@@ -15,6 +15,7 @@ import (
type metadata struct {
readTimeout time.Duration
httpKeepalive bool
proxyProtocol int
sniffing bool
sniffingTimeout time.Duration
@@ -34,6 +35,7 @@ func (h *forwardHandler) parseMetadata(md mdata.Metadata) (err error) {
}
h.md.httpKeepalive = mdutil.GetBool(md, "http.keepalive")
h.md.proxyProtocol = mdutil.GetInt(md, "proxyProtocol")
h.md.sniffing = mdutil.GetBool(md, "sniffing")
h.md.sniffingTimeout = mdutil.GetDuration(md, "sniffing.timeout")
+48 -50
View File
@@ -5,9 +5,7 @@ import (
"bytes"
"context"
"errors"
"fmt"
"net"
"strconv"
"time"
"github.com/go-gost/core/chain"
@@ -16,13 +14,16 @@ import (
mdata "github.com/go-gost/core/metadata"
"github.com/go-gost/core/observer/stats"
"github.com/go-gost/core/recorder"
ctxvalue "github.com/go-gost/x/ctx"
xctx "github.com/go-gost/x/ctx"
ictx "github.com/go-gost/x/internal/ctx"
xnet "github.com/go-gost/x/internal/net"
"github.com/go-gost/x/internal/net/proxyproto"
"github.com/go-gost/x/internal/util/forwarder"
"github.com/go-gost/x/internal/util/sniffing"
tls_util "github.com/go-gost/x/internal/util/tls"
rate_limiter "github.com/go-gost/x/limiter/rate"
mdutil "github.com/go-gost/x/metadata/util"
xstats "github.com/go-gost/x/observer/stats"
stats_wrapper "github.com/go-gost/x/observer/stats/wrapper"
xrecorder "github.com/go-gost/x/recorder"
"github.com/go-gost/x/registry"
@@ -87,26 +88,29 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
LocalAddr: conn.LocalAddr().String(),
Network: "tcp",
Time: start,
SID: string(ctxvalue.SidFromContext(ctx)),
SID: xctx.SidFromContext(ctx).String(),
}
ro.ClientIP = conn.RemoteAddr().String()
if clientAddr := ctxvalue.ClientAddrFromContext(ctx); clientAddr != "" {
ro.ClientIP = string(clientAddr)
if srcAddr := xctx.SrcAddrFromContext(ctx); srcAddr != nil {
ro.ClientAddr = srcAddr.String()
}
if h, _, _ := net.SplitHostPort(ro.ClientIP); h != "" {
ro.ClientIP = h
network := "tcp"
if _, ok := conn.(net.PacketConn); ok {
network = "udp"
}
ro.Network = network
log := h.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"sid": ctxvalue.SidFromContext(ctx),
"client": ro.ClientIP,
"network": ro.Network,
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"client": ro.ClientAddr,
"sid": ro.SID,
})
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
pStats := stats.Stats{}
pStats := xstats.Stats{}
conn = stats_wrapper.WrapConn(conn, &pStats)
defer func() {
@@ -131,12 +135,6 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
return rate_limiter.ErrRateLimit
}
network := "tcp"
if _, ok := conn.(net.PacketConn); ok {
network = "udp"
}
ro.Network = network
var proto string
if network == "tcp" && h.md.sniffing {
if h.md.sniffingTimeout > 0 {
@@ -153,8 +151,15 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
dial := func(ctx context.Context, network, address string) (net.Conn, error) {
var buf bytes.Buffer
cc, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), "tcp", address)
cc, err := h.options.Router.Dial(ictx.ContextWithBuffer(ctx, &buf), "tcp", address)
ro.Route = buf.String()
cc = proxyproto.WrapClientConn(
h.md.proxyProtocol,
xctx.SrcAddrFromContext(ctx),
xctx.DstAddrFromContext(ctx),
cc)
return cc, err
}
sniffer := &forwarder.Sniffer{
@@ -174,6 +179,7 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
switch proto {
case sniffing.ProtoHTTP:
return sniffer.HandleHTTP(ctx, conn,
forwarder.WithService(h.options.Service),
forwarder.WithDial(dial),
forwarder.WithHop(h.hop),
forwarder.WithBypass(h.options.Bypass),
@@ -183,6 +189,7 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
)
case sniffing.ProtoTLS:
return sniffer.HandleTLS(ctx, conn,
forwarder.WithService(h.options.Service),
forwarder.WithDial(dial),
forwarder.WithHop(h.hop),
forwarder.WithBypass(h.options.Bypass),
@@ -193,6 +200,11 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
}
var target *chain.Node
if host := mdutil.GetString(ictx.MetadataFromContext(ctx), "host"); host != "" {
target = &chain.Node{
Addr: host,
}
}
if h.hop != nil {
target = h.hop.Select(ctx,
hop.ProtocolSelectOption(proto),
@@ -216,14 +228,15 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
ro.Host = target.Addr
log = log.WithFields(map[string]any{
"node": target.Name,
"dst": fmt.Sprintf("%s/%s", target.Addr, network),
"node": target.Name,
"dst": target.Addr,
"network": network,
})
log.Debugf("%s >> %s", conn.RemoteAddr(), target.Addr)
var buf bytes.Buffer
cc, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), network, target.Addr)
cc, err := h.options.Router.Dial(ictx.ContextWithBuffer(ctx, &buf), network, target.Addr)
ro.Route = buf.String()
if err != nil {
log.Error(err)
@@ -239,11 +252,20 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
marker.Reset()
}
cc = proxyproto.WrapClientConn(h.md.proxyProtocol, conn.RemoteAddr(), convertAddr(conn.LocalAddr()), cc)
log = log.WithFields(map[string]any{"src": cc.LocalAddr().String(), "dst": cc.RemoteAddr().String()})
ro.SrcAddr = cc.LocalAddr().String()
ro.DstAddr = cc.RemoteAddr().String()
cc = proxyproto.WrapClientConn(
h.md.proxyProtocol,
xctx.SrcAddrFromContext(ctx),
xctx.DstAddrFromContext(ctx),
cc)
t := time.Now()
log.Infof("%s <-> %s", conn.RemoteAddr(), target.Addr)
xnet.Transport(conn, cc)
// xnet.Transport(conn, cc)
xnet.Pipe(ctx, conn, cc)
log.WithFields(map[string]any{
"duration": time.Since(t),
}).Infof("%s >-< %s", conn.RemoteAddr(), target.Addr)
@@ -262,27 +284,3 @@ func (h *forwardHandler) checkRateLimit(addr net.Addr) bool {
return true
}
func convertAddr(addr net.Addr) net.Addr {
host, sp, _ := net.SplitHostPort(addr.String())
ip := net.ParseIP(host)
port, _ := strconv.Atoi(sp)
if ip == nil || ip.Equal(net.IPv6zero) {
ip = net.IPv4zero
}
switch addr.Network() {
case "tcp", "tcp4", "tcp6":
return &net.TCPAddr{
IP: ip,
Port: port,
}
default:
return &net.UDPAddr{
IP: ip,
Port: port,
}
}
}
+142 -128
View File
@@ -11,6 +11,7 @@ import (
"fmt"
"hash/crc32"
"io"
"math"
"net"
"net/http"
"net/http/httputil"
@@ -20,30 +21,35 @@ import (
"time"
"github.com/asaskevich/govalidator"
"github.com/go-gost/core/auth"
"github.com/go-gost/core/bypass"
"github.com/go-gost/core/handler"
"github.com/go-gost/core/limiter"
"github.com/go-gost/core/limiter/traffic"
"github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata"
"github.com/go-gost/core/observer"
"github.com/go-gost/core/observer/stats"
"github.com/go-gost/core/recorder"
xbypass "github.com/go-gost/x/bypass"
ctxvalue "github.com/go-gost/x/ctx"
ctx_internal "github.com/go-gost/x/internal/ctx"
xctx "github.com/go-gost/x/ctx"
ictx "github.com/go-gost/x/internal/ctx"
xio "github.com/go-gost/x/internal/io"
xnet "github.com/go-gost/x/internal/net"
xhttp "github.com/go-gost/x/internal/net/http"
limiter_util "github.com/go-gost/x/internal/util/limiter"
"github.com/go-gost/x/internal/util/sniffing"
stats_util "github.com/go-gost/x/internal/util/stats"
tls_util "github.com/go-gost/x/internal/util/tls"
ws_util "github.com/go-gost/x/internal/util/ws"
rate_limiter "github.com/go-gost/x/limiter/rate"
cache_limiter "github.com/go-gost/x/limiter/traffic/cache"
traffic_wrapper "github.com/go-gost/x/limiter/traffic/wrapper"
xstats "github.com/go-gost/x/observer/stats"
stats_wrapper "github.com/go-gost/x/observer/stats/wrapper"
xrecorder "github.com/go-gost/x/recorder"
"github.com/go-gost/x/registry"
"golang.org/x/net/http/httpguts"
"golang.org/x/time/rate"
)
func init() {
@@ -81,12 +87,16 @@ func (h *httpHandler) Init(md md.Metadata) error {
h.cancel = cancel
if h.options.Observer != nil {
h.stats = stats_util.NewHandlerStats(h.options.Service)
h.stats = stats_util.NewHandlerStats(h.options.Service, h.md.observerResetTraffic)
go h.observeStats(ctx)
}
if limiter := h.options.Limiter; limiter != nil {
h.limiter = limiter_util.NewCachedTrafficLimiter(limiter, 30*time.Second, 60*time.Second)
if h.options.Limiter != nil {
h.limiter = cache_limiter.NewCachedTrafficLimiter(h.options.Limiter,
cache_limiter.RefreshIntervalOption(h.md.limiterRefreshInterval),
cache_limiter.CleanupIntervalOption(h.md.limiterCleanupInterval),
cache_limiter.ScopeOption(limiter.ScopeClient),
)
}
for _, ro := range h.options.Recorders {
@@ -105,6 +115,7 @@ func (h *httpHandler) Init(md md.Metadata) error {
IdleConnTimeout: 30 * time.Second,
ResponseHeaderTimeout: h.md.readTimeout,
DisableKeepAlives: !h.md.keepalive,
DisableCompression: !h.md.compression,
}
return nil
@@ -121,26 +132,23 @@ func (h *httpHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler
LocalAddr: conn.LocalAddr().String(),
Proto: "http",
Time: start,
SID: string(ctxvalue.SidFromContext(ctx)),
SID: xctx.SidFromContext(ctx).String(),
}
ro.ClientIP = conn.RemoteAddr().String()
if clientAddr := ctxvalue.ClientAddrFromContext(ctx); clientAddr != "" {
ro.ClientIP = string(clientAddr)
}
if h, _, _ := net.SplitHostPort(ro.ClientIP); h != "" {
ro.ClientIP = h
if srcAddr := xctx.SrcAddrFromContext(ctx); srcAddr != nil {
ro.ClientAddr = srcAddr.String()
}
log := h.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"sid": ctxvalue.SidFromContext(ctx),
"client": ro.ClientIP,
"network": ro.Network,
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"client": ro.ClientAddr,
"sid": ro.SID,
})
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
pStats := stats.Stats{}
pStats := xstats.Stats{}
conn = stats_wrapper.WrapConn(conn, &pStats)
defer func() {
@@ -220,8 +228,9 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
ro.Host = addr
fields := map[string]any{
"dst": addr,
"host": addr,
"dst": addr,
"host": addr,
"network": network,
}
if u, _, _ := h.basicProxyAuth(req.Header.Get("Proxy-Authorization")); u != "" {
@@ -245,9 +254,6 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
if resp.Header == nil {
resp.Header = http.Header{}
}
if resp.Header.Get("Proxy-Agent") == "" {
resp.Header.Set("Proxy-Agent", h.md.proxyAgent)
}
ro.HTTP = &xrecorder.HTTPRecorderObject{
Host: req.Host,
@@ -281,10 +287,18 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
if !ok {
return errors.New("authentication failed")
}
ctx = ctxvalue.ContextWithClientID(ctx, ctxvalue.ClientID(clientID))
log = log.WithFields(map[string]any{"clientID": clientID})
ro.ClientID = clientID
if resp.Header.Get("Proxy-Agent") == "" {
resp.Header.Set("Proxy-Agent", h.md.proxyAgent)
}
ctx = xctx.ContextWithClientID(ctx, xctx.ClientID(clientID))
if h.options.Bypass != nil &&
h.options.Bypass.Contains(ctx, network, addr) {
h.options.Bypass.Contains(ctx, network, addr, bypass.WithService(h.options.Service)) {
resp.StatusCode = http.StatusForbidden
if log.IsLevelEnabled(logger.TraceLevel) {
@@ -327,8 +341,8 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
return h.handleProxy(ctx, conn, req, ro, log)
}
ctx = ctx_internal.ContextWithRecorderObject(ctx, ro)
ctx = ctxvalue.ContextWithLogger(ctx, log)
ctx = ictx.ContextWithRecorderObject(ctx, ro)
ctx = ictx.ContextWithLogger(ctx, log)
cc, err := h.dial(ctx, "tcp", addr)
if err != nil {
@@ -343,14 +357,17 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
}
defer cc.Close()
resp.StatusCode = http.StatusOK
resp.Status = "200 Connection established"
log = log.WithFields(map[string]any{"src": cc.LocalAddr().String(), "dst": cc.RemoteAddr().String()})
ro.SrcAddr = cc.LocalAddr().String()
ro.DstAddr = cc.RemoteAddr().String()
b := []byte("HTTP/1.1 200 Connection established\r\n" +
"Proxy-Agent: " + h.md.proxyAgent + "\r\n\r\n")
if log.IsLevelEnabled(logger.TraceLevel) {
dump, _ := httputil.DumpResponse(resp, false)
log.Trace(string(dump))
log.Trace(string(b))
}
if err = resp.Write(conn); err != nil {
if _, err = conn.Write(b); err != nil {
log.Error(err)
return err
}
@@ -390,14 +407,16 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
conn = xnet.NewReadWriteConn(br, conn, conn)
switch proto {
case sniffing.ProtoHTTP:
return sniffer.HandleHTTP(ctx, conn,
return sniffer.HandleHTTP(ctx, "tcp", conn,
sniffing.WithService(h.options.Service),
sniffing.WithDial(dial),
sniffing.WithDialTLS(dialTLS),
sniffing.WithRecorderObject(ro),
sniffing.WithLog(log),
)
case sniffing.ProtoTLS:
return sniffer.HandleTLS(ctx, conn,
return sniffer.HandleTLS(ctx, "tcp", conn,
sniffing.WithService(h.options.Service),
sniffing.WithDial(dial),
sniffing.WithDialTLS(dialTLS),
sniffing.WithRecorderObject(ro),
@@ -408,7 +427,8 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
start := time.Now()
log.Infof("%s <-> %s", conn.RemoteAddr(), addr)
xnet.Transport(conn, cc)
// xnet.Transport(conn, cc)
xnet.Pipe(ctx, conn, cc)
log.WithFields(map[string]any{
"duration": time.Since(start),
}).Infof("%s >-< %s", conn.RemoteAddr(), addr)
@@ -417,12 +437,12 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
}
func (h *httpHandler) handleProxy(ctx context.Context, conn net.Conn, req *http.Request, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
pStats := stats.Stats{}
pStats := xstats.Stats{}
conn = stats_wrapper.WrapConn(conn, &pStats)
ro.Time = time.Time{}
if err := h.proxyRoundTrip(ctx, conn, req, ro, &pStats, log); err != nil {
if close, err := h.proxyRoundTrip(ctx, conn, req, ro, &pStats, log); err != nil || close {
return err
}
@@ -443,13 +463,15 @@ func (h *httpHandler) handleProxy(ctx context.Context, conn net.Conn, req *http.
log.Trace(string(dump))
}
if err := h.proxyRoundTrip(ctx, xio.NewReadWriter(br, conn), req, ro, &pStats, log); err != nil {
if close, err := h.proxyRoundTrip(ctx, xio.NewReadWriteCloser(br, conn, conn), req, ro, &pStats, log); err != nil || close {
return err
}
}
}
func (h *httpHandler) proxyRoundTrip(ctx context.Context, rw io.ReadWriter, req *http.Request, ro *xrecorder.HandlerRecorderObject, pStats *stats.Stats, log logger.Logger) (err error) {
func (h *httpHandler) proxyRoundTrip(ctx context.Context, rw io.ReadWriteCloser, req *http.Request, ro *xrecorder.HandlerRecorderObject, pStats stats.Stats, log logger.Logger) (close bool, err error) {
close = true
ro2 := &xrecorder.HandlerRecorderObject{}
*ro2 = *ro
ro = ro2
@@ -495,19 +517,10 @@ func (h *httpHandler) proxyRoundTrip(ctx context.Context, rw io.ReadWriter, req
Header: req.Header.Clone(),
},
}
if clientIP := xhttp.GetClientIP(req); clientIP != nil {
ro.ClientIP = clientIP.String()
}
clientAddr := ro.RemoteAddr
if ro.ClientIP != "" {
if _, port, _ := net.SplitHostPort(ro.RemoteAddr); port != "" {
clientAddr = net.JoinHostPort(ro.ClientIP, port)
}
}
// HTTP/1.0
if req.ProtoMajor == 1 && req.ProtoMinor == 0 {
http10 := req.ProtoMajor == 1 && req.ProtoMinor == 0
if http10 {
if strings.ToLower(req.Header.Get("Connection")) == "keep-alive" {
req.Header.Del("Connection")
} else {
@@ -529,7 +542,7 @@ func (h *httpHandler) proxyRoundTrip(ctx context.Context, rw io.ReadWriter, req
ro.HTTP.StatusCode = res.StatusCode
if h.options.Bypass != nil &&
h.options.Bypass.Contains(ctx, "tcp", host) {
h.options.Bypass.Contains(ctx, "tcp", host, bypass.WithService(h.options.Service)) {
res.StatusCode = http.StatusForbidden
if log.IsLevelEnabled(logger.TraceLevel) {
@@ -538,7 +551,8 @@ func (h *httpHandler) proxyRoundTrip(ctx context.Context, rw io.ReadWriter, req
}
log.Debug("bypass: ", host)
res.Write(rw)
return xbypass.ErrBypass
err = xbypass.ErrBypass
return
}
var reqBody *xhttp.Body
@@ -556,22 +570,22 @@ func (h *httpHandler) proxyRoundTrip(ctx context.Context, rw io.ReadWriter, req
}
}
ctx = ctxvalue.ContextWithClientAddr(ctx, ctxvalue.ClientAddr(clientAddr))
ctx = ctx_internal.ContextWithRecorderObject(ctx, ro)
ctx = ctxvalue.ContextWithLogger(ctx, log)
ctx = ictx.ContextWithRecorderObject(ctx, ro)
ctx = ictx.ContextWithLogger(ctx, log)
resp, err := h.transport.RoundTrip(req.WithContext(ctx))
if err != nil {
res.Write(rw)
return
}
defer resp.Body.Close()
if reqBody != nil {
ro.HTTP.Request.Body = reqBody.Content()
ro.HTTP.Request.ContentLength = reqBody.Length()
}
if err != nil {
res.Write(rw)
return
}
defer resp.Body.Close()
ro.HTTP.StatusCode = resp.StatusCode
ro.HTTP.Response.Header = resp.Header.Clone()
ro.HTTP.Response.ContentLength = resp.ContentLength
@@ -582,16 +596,17 @@ func (h *httpHandler) proxyRoundTrip(ctx context.Context, rw io.ReadWriter, req
}
// HTTP/1.0
if req.ProtoMajor == 1 && req.ProtoMinor == 0 {
if http10 {
if !resp.Close {
resp.Header.Set("Connection", "keep-alive")
}
resp.ProtoMajor = req.ProtoMajor
resp.ProtoMinor = req.ProtoMinor
resp.ProtoMajor = 1
resp.ProtoMinor = 0
}
if resp.StatusCode == http.StatusSwitchingProtocols {
return h.handleUpgradeResponse(ctx, rw, req, resp, ro, log)
err = h.handleUpgradeResponse(ctx, rw, req, resp, ro, log)
return
}
var respBody *xhttp.Body
@@ -607,32 +622,44 @@ func (h *httpHandler) proxyRoundTrip(ctx context.Context, rw io.ReadWriter, req
resp.Body = respBody
}
if err = resp.Write(rw); err != nil {
return fmt.Errorf("write response: %v", err)
}
err = resp.Write(rw)
if respBody != nil {
ro.HTTP.Response.Body = respBody.Content()
ro.HTTP.Response.ContentLength = respBody.Length()
}
if err != nil {
err = fmt.Errorf("write response: %v", err)
return
}
if resp.ContentLength >= 0 {
close = resp.Close
}
return
}
func (h *httpHandler) dial(ctx context.Context, network, addr string) (conn net.Conn, err error) {
switch h.md.hash {
case "host":
ctx = ctxvalue.ContextWithHash(ctx, &ctxvalue.Hash{Source: addr})
ctx = xctx.ContextWithHash(ctx, &xctx.Hash{Source: addr})
}
if log := ctxvalue.LoggerFromContext(ctx); log != nil {
if log := ictx.LoggerFromContext(ctx); log != nil {
log.Debugf("dial: new connection to host %s", addr)
}
var buf bytes.Buffer
conn, err = h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), network, addr)
if ro := ctx_internal.RecorderObjectFromContext(ctx); ro != nil {
conn, err = h.options.Router.Dial(ictx.ContextWithBuffer(ctx, &buf), network, addr)
if ro := ictx.RecorderObjectFromContext(ctx); ro != nil {
ro.Route = buf.String()
if conn != nil {
ro.SrcAddr = conn.LocalAddr().String()
ro.DstAddr = conn.RemoteAddr().String()
}
}
return
@@ -645,7 +672,7 @@ func upgradeType(h http.Header) string {
return h.Get("Upgrade")
}
func (h *httpHandler) handleUpgradeResponse(ctx context.Context, rw io.ReadWriter, req *http.Request, res *http.Response, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
func (h *httpHandler) handleUpgradeResponse(ctx context.Context, rw io.ReadWriteCloser, req *http.Request, res *http.Response, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
reqUpType := upgradeType(req.Header)
resUpType := upgradeType(res.Header)
if !strings.EqualFold(reqUpType, resUpType) {
@@ -666,7 +693,8 @@ func (h *httpHandler) handleUpgradeResponse(ctx context.Context, rw io.ReadWrite
return h.sniffingWebsocketFrame(ctx, rw, backConn, ro, log)
}
return xnet.Transport(rw, backConn)
// return xnet.Transport(rw, backConn)
return xnet.Pipe(ctx, rw, backConn)
}
func (h *httpHandler) sniffingWebsocketFrame(ctx context.Context, rw, cc io.ReadWriter, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
@@ -676,44 +704,33 @@ func (h *httpHandler) sniffingWebsocketFrame(ctx context.Context, rw, cc io.Read
if sampleRate == 0 {
sampleRate = sniffing.DefaultSampleRate
}
d := time.Duration(1 / sampleRate * 1e9)
if sampleRate < 0 {
sampleRate = math.MaxFloat64
}
go func() {
ro2 := &xrecorder.HandlerRecorderObject{}
*ro2 = *ro
ro := ro2
ticker := &time.Ticker{}
if d > 0 {
ticker = time.NewTicker(d)
defer ticker.Stop()
} else {
tc := make(chan time.Time)
close(tc)
ticker.C = tc
}
limiter := rate.NewLimiter(rate.Limit(sampleRate), int(sampleRate))
buf := &bytes.Buffer{}
for {
start := time.Now()
err := h.copyWebsocketFrame(cc, rw, buf, "client", ro)
select {
case <-ticker.C:
if err == nil {
ro.Duration = time.Since(start)
ro.Time = time.Now()
if err := ro.Record(ctx, h.recorder.Recorder); err != nil {
log.Errorf("record: %v", err)
}
}
default:
}
if err != nil {
if err := h.copyWebsocketFrame(cc, rw, buf, "client", ro); err != nil {
errc <- err
return
}
if limiter.Allow() {
ro.Duration = time.Since(start)
ro.Time = time.Now()
if err := ro.Record(ctx, h.recorder.Recorder); err != nil {
log.Errorf("record: %v", err)
}
}
}
}()
@@ -722,37 +739,24 @@ func (h *httpHandler) sniffingWebsocketFrame(ctx context.Context, rw, cc io.Read
*ro2 = *ro
ro := ro2
ticker := &time.Ticker{}
if d > 0 {
ticker = time.NewTicker(d)
defer ticker.Stop()
} else {
tc := make(chan time.Time)
close(tc)
ticker.C = tc
}
limiter := rate.NewLimiter(rate.Limit(sampleRate), int(sampleRate))
buf := &bytes.Buffer{}
for {
start := time.Now()
err := h.copyWebsocketFrame(rw, cc, buf, "server", ro)
select {
case <-ticker.C:
if err == nil {
ro.Duration = time.Since(start)
ro.Time = time.Now()
if err := ro.Record(ctx, h.recorder.Recorder); err != nil {
log.Errorf("record: %v", err)
}
}
default:
}
if err != nil {
if err := h.copyWebsocketFrame(rw, cc, buf, "server", ro); err != nil {
errc <- err
return
}
if limiter.Allow() {
ro.Duration = time.Since(start)
ro.Time = time.Now()
if err := ro.Record(ctx, h.recorder.Recorder); err != nil {
log.Errorf("record: %v", err)
}
}
}
}()
@@ -855,7 +859,7 @@ func (h *httpHandler) authenticate(ctx context.Context, conn net.Conn, req *http
if h.options.Auther == nil {
return "", true
}
if id, ok = h.options.Auther.Authenticate(ctx, u, p); ok {
if id, ok = h.options.Auther.Authenticate(ctx, u, p, auth.WithService(h.options.Service)); ok {
return
}
@@ -888,7 +892,8 @@ func (h *httpHandler) authenticate(ctx context.Context, conn net.Conn, req *http
defer cc.Close()
req.Write(cc)
xnet.Transport(conn, cc)
// xnet.Transport(conn, cc)
xnet.Pipe(ctx, conn, cc)
return
case "file":
f, _ := os.Open(pr.Value)
@@ -957,17 +962,26 @@ func (h *httpHandler) observeStats(ctx context.Context) {
return
}
d := h.md.observePeriod
if d < time.Millisecond {
d = 5 * time.Second
}
ticker := time.NewTicker(d)
var events []observer.Event
ticker := time.NewTicker(h.md.observerPeriod)
defer ticker.Stop()
for {
select {
case <-ticker.C:
h.options.Observer.Observe(ctx, h.stats.Events())
if len(events) > 0 {
if err := h.options.Observer.Observe(ctx, events); err == nil {
events = nil
}
break
}
evs := h.stats.Events()
if err := h.options.Observer.Observe(ctx, evs); err != nil {
events = evs
}
case <-ctx.Done():
return
}
+27 -7
View File
@@ -22,14 +22,18 @@ const (
type metadata struct {
readTimeout time.Duration
keepalive bool
compression bool
probeResistance *probeResistance
enableUDP bool
udpBufferSize int
header http.Header
hash string
authBasicRealm string
observePeriod time.Duration
proxyAgent string
observerPeriod time.Duration
observerResetTraffic bool
sniffing bool
sniffingTimeout time.Duration
sniffingWebsocket bool
@@ -39,13 +43,19 @@ type metadata struct {
privateKey crypto.PrivateKey
alpn string
mitmBypass bypass.Bypass
limiterRefreshInterval time.Duration
limiterCleanupInterval time.Duration
}
func (h *httpHandler) parseMetadata(md mdata.Metadata) error {
h.md.readTimeout = mdutil.GetDuration(md, "readTimeout")
if h.md.readTimeout <= 0 {
if h.md.readTimeout == 0 {
h.md.readTimeout = 15 * time.Second
}
if h.md.readTimeout < 0 {
h.md.readTimeout = 0
}
if m := mdutil.GetStringMapString(md, "http.header", "header"); len(m) > 0 {
hd := http.Header{}
@@ -55,10 +65,8 @@ func (h *httpHandler) parseMetadata(md mdata.Metadata) error {
h.md.header = hd
}
h.md.keepalive = true
if mdutil.IsExists(md, "http.keepalive", "keepalive") {
h.md.keepalive = mdutil.GetBool(md, "http.keepalive", "keepalive")
}
h.md.keepalive = mdutil.GetBool(md, "http.keepalive", "keepalive")
h.md.compression = mdutil.GetBool(md, "http.compression", "compression")
if pr := mdutil.GetString(md, "probeResist", "probe_resist"); pr != "" {
if ss := strings.SplitN(pr, ":", 2); len(ss) == 2 {
@@ -70,10 +78,19 @@ func (h *httpHandler) parseMetadata(md mdata.Metadata) error {
}
}
h.md.enableUDP = mdutil.GetBool(md, "udp")
h.md.udpBufferSize = mdutil.GetInt(md, "udp.bufferSize", "udpBufferSize")
h.md.hash = mdutil.GetString(md, "hash")
h.md.authBasicRealm = mdutil.GetString(md, "authBasicRealm")
h.md.observePeriod = mdutil.GetDuration(md, "observePeriod")
h.md.observerPeriod = mdutil.GetDuration(md, "observePeriod", "observer.period", "observer.observePeriod")
if h.md.observerPeriod == 0 {
h.md.observerPeriod = 5 * time.Second
}
if h.md.observerPeriod < time.Second {
h.md.observerPeriod = time.Second
}
h.md.observerResetTraffic = mdutil.GetBool(md, "observer.resetTraffic")
h.md.proxyAgent = mdutil.GetString(md, "http.proxyAgent", "proxyAgent")
if h.md.proxyAgent == "" {
@@ -101,6 +118,9 @@ func (h *httpHandler) parseMetadata(md mdata.Metadata) error {
h.md.alpn = mdutil.GetString(md, "mitm.alpn")
h.md.mitmBypass = registry.BypassRegistry().Get(mdutil.GetString(md, "mitm.bypass"))
h.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
h.md.limiterCleanupInterval = mdutil.GetDuration(md, "limiter.cleanupInterval")
return nil
}
+7 -2
View File
@@ -10,7 +10,7 @@ import (
"time"
"github.com/go-gost/core/logger"
ctxvalue "github.com/go-gost/x/ctx"
ictx "github.com/go-gost/x/internal/ctx"
"github.com/go-gost/x/internal/net/udp"
"github.com/go-gost/x/internal/util/socks"
xrecorder "github.com/go-gost/x/recorder"
@@ -55,7 +55,7 @@ func (h *httpHandler) handleUDP(ctx context.Context, conn net.Conn, ro *xrecorde
// obtain a udp connection
var buf bytes.Buffer
c, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), "udp", "") // UDP association
c, err := h.options.Router.Dial(ictx.ContextWithBuffer(ctx, &buf), "udp", "") // UDP association
ro.Route = buf.String()
if err != nil {
log.Error(err)
@@ -63,6 +63,9 @@ func (h *httpHandler) handleUDP(ctx context.Context, conn net.Conn, ro *xrecorde
}
defer c.Close()
log.WithFields(map[string]any{"src": c.LocalAddr().String()})
ro.SrcAddr = c.LocalAddr().String()
pc, ok := c.(net.PacketConn)
if !ok {
err = errors.New("wrong connection type")
@@ -71,7 +74,9 @@ func (h *httpHandler) handleUDP(ctx context.Context, conn net.Conn, ro *xrecorde
}
relay := udp.NewRelay(socks.UDPTunServerConn(conn), pc).
WithService(h.options.Service).
WithBypass(h.options.Bypass).
WithBufferSize(h.md.udpBufferSize).
WithLogger(log)
t := time.Now()
+115 -82
View File
@@ -18,21 +18,25 @@ import (
"strings"
"time"
"github.com/go-gost/core/auth"
"github.com/go-gost/core/bypass"
"github.com/go-gost/core/handler"
"github.com/go-gost/core/limiter"
"github.com/go-gost/core/limiter/traffic"
"github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata"
"github.com/go-gost/core/observer"
"github.com/go-gost/core/observer/stats"
"github.com/go-gost/core/recorder"
xbypass "github.com/go-gost/x/bypass"
ctxvalue "github.com/go-gost/x/ctx"
xctx "github.com/go-gost/x/ctx"
ictx "github.com/go-gost/x/internal/ctx"
xio "github.com/go-gost/x/internal/io"
xnet "github.com/go-gost/x/internal/net"
xhttp "github.com/go-gost/x/internal/net/http"
limiter_util "github.com/go-gost/x/internal/util/limiter"
stats_util "github.com/go-gost/x/internal/util/stats"
rate_limiter "github.com/go-gost/x/limiter/rate"
cache_limiter "github.com/go-gost/x/limiter/traffic/cache"
traffic_wrapper "github.com/go-gost/x/limiter/traffic/wrapper"
stats_wrapper "github.com/go-gost/x/observer/stats/wrapper"
xrecorder "github.com/go-gost/x/recorder"
@@ -72,12 +76,16 @@ func (h *http2Handler) Init(md md.Metadata) error {
h.cancel = cancel
if h.options.Observer != nil {
h.stats = stats_util.NewHandlerStats(h.options.Service)
h.stats = stats_util.NewHandlerStats(h.options.Service, h.md.observerResetTraffic)
go h.observeStats(ctx)
}
if limiter := h.options.Limiter; limiter != nil {
h.limiter = limiter_util.NewCachedTrafficLimiter(limiter, 30*time.Second, 60*time.Second)
if h.options.Limiter != nil {
h.limiter = cache_limiter.NewCachedTrafficLimiter(h.options.Limiter,
cache_limiter.RefreshIntervalOption(h.md.limiterRefreshInterval),
cache_limiter.CleanupIntervalOption(h.md.limiterCleanupInterval),
cache_limiter.ScopeOption(limiter.ScopeClient),
)
}
for _, ro := range h.options.Recorders {
@@ -101,14 +109,18 @@ func (h *http2Handler) Handle(ctx context.Context, conn net.Conn, opts ...handle
LocalAddr: conn.LocalAddr().String(),
Network: "tcp",
Time: start,
SID: string(ctxvalue.SidFromContext(ctx)),
SID: xctx.SidFromContext(ctx).String(),
}
if srcAddr := xctx.SrcAddrFromContext(ctx); srcAddr != nil {
ro.ClientAddr = srcAddr.String()
}
ro.ClientIP, _, _ = net.SplitHostPort(conn.RemoteAddr().String())
log := h.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"sid": ctxvalue.SidFromContext(ctx),
"network": ro.Network,
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"client": ro.ClientAddr,
"sid": ro.SID,
})
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
defer func() {
@@ -127,20 +139,17 @@ func (h *http2Handler) Handle(ctx context.Context, conn net.Conn, opts ...handle
return rate_limiter.ErrRateLimit
}
v, ok := conn.(md.Metadatable)
if !ok || v == nil {
err = errors.New("wrong connection type")
md := ictx.MetadataFromContext(ctx)
if md == nil {
err = errors.New("http2: wrong connection type")
log.Error(err)
return err
}
md := v.Metadata()
return h.roundTrip(ctx,
md.Get("w").(http.ResponseWriter),
md.Get("r").(*http.Request),
ro,
log,
)
w, _ := md.Get("w").(http.ResponseWriter)
r, _ := md.Get("r").(*http.Request)
return h.roundTrip(ctx, w, r, ro, log)
}
func (h *http2Handler) Close() error {
@@ -154,6 +163,10 @@ func (h *http2Handler) Close() error {
// when server returns an non-200 status code,
// May be fixed in go1.18.
func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req *http.Request, ro *xrecorder.HandlerRecorderObject, log logger.Logger) error {
if w == nil || req == nil {
return nil
}
// Try to get the actual host.
// Compatible with GOST 2.x.
if v := req.Header.Get("Gost-Target"); v != "" {
@@ -224,9 +237,13 @@ func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
if !ok {
return errors.New("authentication failed")
}
ctx = ctxvalue.ContextWithClientID(ctx, ctxvalue.ClientID(clientID))
if h.options.Bypass != nil && h.options.Bypass.Contains(ctx, "tcp", host) {
log = log.WithFields(map[string]any{"clientID": clientID})
ro.ClientID = clientID
ctx = xctx.ContextWithClientID(ctx, xctx.ClientID(clientID))
if h.options.Bypass != nil && h.options.Bypass.Contains(ctx, "tcp", host, bypass.WithService(h.options.Service)) {
resp.StatusCode = http.StatusForbidden
w.WriteHeader(resp.StatusCode)
log.Debug("bypass: ", host)
@@ -241,11 +258,11 @@ func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
switch h.md.hash {
case "host":
ctx = ctxvalue.ContextWithHash(ctx, &ctxvalue.Hash{Source: host})
ctx = xctx.ContextWithHash(ctx, &xctx.Hash{Source: host})
}
var buf bytes.Buffer
cc, err := h.options.Router.Dial(ctxvalue.ContextWithBuffer(ctx, &buf), "tcp", host)
cc, err := h.options.Router.Dial(ictx.ContextWithBuffer(ctx, &buf), "tcp", host)
ro.Route = buf.String()
if err != nil {
log.Error(err)
@@ -255,66 +272,72 @@ func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
}
defer cc.Close()
if req.Method == http.MethodConnect {
resp.StatusCode = http.StatusOK
w.WriteHeader(http.StatusOK)
if fw, ok := w.(http.Flusher); ok {
fw.Flush()
}
// compatible with HTTP1.x
if hj, ok := w.(http.Hijacker); ok && req.ProtoMajor == 1 {
// we take over the underly connection
conn, _, err := hj.Hijack()
if err != nil {
log.Error(err)
resp.StatusCode = http.StatusInternalServerError
w.WriteHeader(http.StatusInternalServerError)
return err
}
defer conn.Close()
start := time.Now()
log.Infof("%s <-> %s", conn.RemoteAddr(), host)
xnet.Transport(conn, cc)
log.WithFields(map[string]any{
"duration": time.Since(start),
}).Infof("%s >-< %s", conn.RemoteAddr(), host)
return nil
}
rw := traffic_wrapper.WrapReadWriter(
h.limiter,
xio.NewReadWriter(req.Body, flushWriter{w}),
clientID,
limiter.ScopeOption(limiter.ScopeClient),
limiter.ServiceOption(h.options.Service),
limiter.NetworkOption("tcp"),
limiter.AddrOption(host),
limiter.ClientOption(clientID),
limiter.SrcOption(req.RemoteAddr),
)
if h.options.Observer != nil {
pstats := h.stats.Stats(clientID)
pstats.Add(stats.KindTotalConns, 1)
pstats.Add(stats.KindCurrentConns, 1)
defer pstats.Add(stats.KindCurrentConns, -1)
rw = stats_wrapper.WrapReadWriter(rw, pstats)
}
log = log.WithFields(map[string]any{"src": cc.LocalAddr().String(), "dst": cc.RemoteAddr().String()})
ro.SrcAddr = cc.LocalAddr().String()
ro.DstAddr = cc.RemoteAddr().String()
if req.Method != http.MethodConnect {
start := time.Now()
log.Infof("%s <-> %s", req.RemoteAddr, host)
xnet.Transport(rw, cc)
if err := h.forwardRequest(w, req, cc); err != nil {
log.Info("%s - %s: %s", req.RemoteAddr, host, err)
}
log.WithFields(map[string]any{
"duration": time.Since(start),
}).Infof("%s >-< %s", req.RemoteAddr, host)
return nil
}
// TODO: forward request
resp.StatusCode = http.StatusBadRequest
w.WriteHeader(resp.StatusCode)
resp.StatusCode = http.StatusOK
w.WriteHeader(http.StatusOK)
if fw, ok := w.(http.Flusher); ok {
fw.Flush()
}
rw := xio.NewReadWriter(req.Body, flushWriter{w})
// compatible with HTTP1.x
if hj, ok := w.(http.Hijacker); ok && req.ProtoMajor == 1 {
// we take over the underly connection
conn, _, err := hj.Hijack()
if err != nil {
log.Error(err)
resp.StatusCode = http.StatusInternalServerError
w.WriteHeader(http.StatusInternalServerError)
return err
}
defer conn.Close()
rw = conn
}
rw = traffic_wrapper.WrapReadWriter(
h.limiter,
rw,
clientID,
limiter.ScopeOption(limiter.ScopeClient),
limiter.ServiceOption(h.options.Service),
limiter.NetworkOption("tcp"),
limiter.AddrOption(host),
limiter.ClientOption(clientID),
limiter.SrcOption(req.RemoteAddr),
)
if h.options.Observer != nil {
pstats := h.stats.Stats(clientID)
pstats.Add(stats.KindTotalConns, 1)
pstats.Add(stats.KindCurrentConns, 1)
defer pstats.Add(stats.KindCurrentConns, -1)
rw = stats_wrapper.WrapReadWriter(rw, pstats)
}
start := time.Now()
log.Infof("%s <-> %s", req.RemoteAddr, host)
// xnet.Transport(rw, cc)
xnet.Pipe(ctx, xio.NewReadWriteCloser(rw, rw, req.Body), cc)
log.WithFields(map[string]any{
"duration": time.Since(start),
}).Infof("%s >-< %s", req.RemoteAddr, host)
return nil
}
@@ -362,7 +385,7 @@ func (h *http2Handler) authenticate(ctx context.Context, w http.ResponseWriter,
if h.options.Auther == nil {
return "", true
}
if id, ok = h.options.Auther.Authenticate(ctx, u, p); ok {
if id, ok = h.options.Auther.Authenticate(ctx, u, p, auth.WithService(h.options.Service)); ok {
return
}
@@ -445,6 +468,7 @@ func (h *http2Handler) authenticate(ctx context.Context, w http.ResponseWriter,
return
}
func (h *http2Handler) forwardRequest(w http.ResponseWriter, r *http.Request, rw io.ReadWriter) (err error) {
if err = r.Write(rw); err != nil {
return
@@ -487,17 +511,26 @@ func (h *http2Handler) observeStats(ctx context.Context) {
return
}
d := h.md.observePeriod
if d < time.Millisecond {
d = 5 * time.Second
}
ticker := time.NewTicker(d)
var events []observer.Event
ticker := time.NewTicker(h.md.observerPeriod)
defer ticker.Stop()
for {
select {
case <-ticker.C:
h.options.Observer.Observe(ctx, h.stats.Events())
if len(events) > 0 {
if err := h.options.Observer.Observe(ctx, events); err == nil {
events = nil
}
break
}
evs := h.stats.Events()
if err := h.options.Observer.Observe(ctx, evs); err != nil {
events = evs
}
case <-ctx.Done():
return
}
+20 -6
View File
@@ -14,11 +14,15 @@ const (
)
type metadata struct {
probeResistance *probeResistance
header http.Header
hash string
authBasicRealm string
observePeriod time.Duration
probeResistance *probeResistance
header http.Header
hash string
authBasicRealm string
observerPeriod time.Duration
observerResetTraffic bool
limiterRefreshInterval time.Duration
limiterCleanupInterval time.Duration
}
func (h *http2Handler) parseMetadata(md mdata.Metadata) error {
@@ -42,7 +46,17 @@ func (h *http2Handler) parseMetadata(md mdata.Metadata) error {
h.md.hash = mdutil.GetString(md, "hash")
h.md.authBasicRealm = mdutil.GetString(md, "authBasicRealm")
h.md.observePeriod = mdutil.GetDuration(md, "observePeriod")
h.md.observerPeriod = mdutil.GetDuration(md, "observePeriod", "observer.period", "observer.observePeriod")
if h.md.observerPeriod == 0 {
h.md.observerPeriod = 5 * time.Second
}
if h.md.observerPeriod < time.Second {
h.md.observerPeriod = time.Second
}
h.md.observerResetTraffic = mdutil.GetBool(md, "observer.resetTraffic")
h.md.limiterRefreshInterval = mdutil.GetDuration(md, "limiter.refreshInterval")
h.md.limiterCleanupInterval = mdutil.GetDuration(md, "limiter.cleanupInterval")
return nil
}
+22 -17
View File
@@ -3,19 +3,20 @@ package http3
import (
"context"
"errors"
"fmt"
"net"
"net/http"
"net/http/httputil"
"strings"
"time"
"github.com/go-gost/core/bypass"
"github.com/go-gost/core/chain"
"github.com/go-gost/core/handler"
"github.com/go-gost/core/hop"
"github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata"
ctxvalue "github.com/go-gost/x/ctx"
xctx "github.com/go-gost/x/ctx"
ictx "github.com/go-gost/x/internal/ctx"
"github.com/go-gost/x/registry"
)
@@ -58,9 +59,10 @@ func (h *http3Handler) Handle(ctx context.Context, conn net.Conn, opts ...handle
start := time.Now()
log := h.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"sid": ctxvalue.SidFromContext(ctx),
"network": "udp",
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"sid": xctx.SidFromContext(ctx).String(),
})
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
defer func() {
@@ -73,21 +75,24 @@ func (h *http3Handler) Handle(ctx context.Context, conn net.Conn, opts ...handle
return nil
}
v, ok := conn.(md.Metadatable)
if !ok || v == nil {
err := errors.New("wrong connection type")
md := ictx.MetadataFromContext(ctx)
if md == nil {
err := errors.New("http3: wrong connection type")
log.Error(err)
return err
}
md := v.Metadata()
return h.roundTrip(ctx,
md.Get("w").(http.ResponseWriter),
md.Get("r").(*http.Request),
log,
)
w, _ := md.Get("w").(http.ResponseWriter)
r, _ := md.Get("r").(*http.Request)
return h.roundTrip(ctx, w, r, log)
}
func (h *http3Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req *http.Request, log logger.Logger) error {
if w == nil || req == nil {
return nil
}
addr := req.Host
if _, port, _ := net.SplitHostPort(addr); port == "" {
addr = net.JoinHostPort(strings.Trim(addr, "[]"), "80")
@@ -102,7 +107,7 @@ func (h *http3Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
w.Header().Set(k, h.md.header.Get(k))
}
if h.options.Bypass != nil && h.options.Bypass.Contains(ctx, "udp", addr) {
if h.options.Bypass != nil && h.options.Bypass.Contains(ctx, "udp", addr, bypass.WithService(h.options.Service)) {
w.WriteHeader(http.StatusForbidden)
log.Debug("bypass: ", addr)
return nil
@@ -110,7 +115,7 @@ func (h *http3Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
switch h.md.hash {
case "host":
ctx = ctxvalue.ContextWithHash(ctx, &ctxvalue.Hash{Source: addr})
ctx = xctx.ContextWithHash(ctx, &xctx.Hash{Source: addr})
}
var target *chain.Node
@@ -124,7 +129,7 @@ func (h *http3Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
}
log = log.WithFields(map[string]any{
"dst": fmt.Sprintf("%s/%s", target.Addr, "tcp"),
"dst": target.Addr,
"host": target.Addr,
})
+4 -15
View File
@@ -15,15 +15,7 @@ type metadata struct {
}
func (h *http3Handler) parseMetadata(md mdata.Metadata) error {
const (
header = "header"
probeResistKey = "probeResistance"
probeResistKeyX = "probe_resist"
knock = "knock"
hash = "hash"
)
if m := mdutil.GetStringMapString(md, header); len(m) > 0 {
if m := mdutil.GetStringMapString(md, "header"); len(m) > 0 {
hd := http.Header{}
for k, v := range m {
hd.Add(k, v)
@@ -31,20 +23,17 @@ func (h *http3Handler) parseMetadata(md mdata.Metadata) error {
h.md.header = hd
}
pr := mdutil.GetString(md, probeResistKey)
if pr == "" {
pr = mdutil.GetString(md, probeResistKeyX)
}
pr := mdutil.GetString(md, "probeResistance", "probe_resist")
if pr != "" {
if ss := strings.SplitN(pr, ":", 2); len(ss) == 2 {
h.md.probeResistance = &probeResistance{
Type: ss[0],
Value: ss[1],
Knock: mdutil.GetString(md, knock),
Knock: mdutil.GetString(md, "knock"),
}
}
}
h.md.hash = mdutil.GetString(md, hash)
h.md.hash = mdutil.GetString(md, "hash")
return nil
}
+24 -42
View File
@@ -1,12 +1,12 @@
package file
package metrics
import (
"context"
"net"
"net/http"
"sync"
"time"
"github.com/go-gost/core/auth"
"github.com/go-gost/core/handler"
md "github.com/go-gost/core/metadata"
xmetrics "github.com/go-gost/x/metrics"
@@ -20,8 +20,7 @@ func init() {
type metricsHandler struct {
handler http.Handler
server *http.Server
ln *singleConnListener
mux *http.ServeMux
md metadata
options handler.Options
}
@@ -42,38 +41,39 @@ func (h *metricsHandler) Init(md md.Metadata) (err error) {
return
}
xmetrics.Init(xmetrics.NewMetrics())
xmetrics.Enable(true)
h.handler = promhttp.Handler()
mux := http.NewServeMux()
mux.Handle(h.md.path, http.HandlerFunc(h.handleFunc))
h.server = &http.Server{
Handler: mux,
}
h.ln = &singleConnListener{
conn: make(chan net.Conn),
done: make(chan struct{}),
}
go h.server.Serve(h.ln)
h.mux = mux
return
}
func (h *metricsHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.HandleOption) error {
h.ln.send(conn)
l := &singleConnListener{
conn: make(chan net.Conn, 1),
}
l.send(conn)
return nil
s := http.Server{
Handler: h.mux,
}
s.Serve(l)
return s.Shutdown(ctx)
}
func (h *metricsHandler) Close() error {
return h.server.Close()
return nil
}
func (h *metricsHandler) handleFunc(w http.ResponseWriter, r *http.Request) {
if auther := h.options.Auther; auther != nil {
u, p, _ := r.BasicAuth()
if _, ok := auther.Authenticate(r.Context(), u, p); !ok {
if _, ok := auther.Authenticate(r.Context(), u, p, auth.WithService(h.options.Service)); !ok {
w.WriteHeader(http.StatusUnauthorized)
return
}
@@ -88,47 +88,29 @@ func (h *metricsHandler) handleFunc(w http.ResponseWriter, r *http.Request) {
"remote": r.RemoteAddr,
"duration": time.Since(start),
})
log.Debugf("%s %s", r.Method, r.RequestURI)
log.Infof("%s %s", r.Method, r.RequestURI)
}
type singleConnListener struct {
conn chan net.Conn
addr net.Addr
done chan struct{}
mu sync.Mutex
}
func (l *singleConnListener) Accept() (net.Conn, error) {
select {
case conn := <-l.conn:
if conn, ok := <-l.conn; ok {
return conn, nil
case <-l.done:
return nil, net.ErrClosed
}
return nil, net.ErrClosed
}
func (l *singleConnListener) Close() error {
l.mu.Lock()
defer l.mu.Unlock()
select {
case <-l.done:
default:
close(l.done)
}
return nil
}
func (l *singleConnListener) Addr() net.Addr {
return l.addr
return &net.TCPAddr{}
}
func (l *singleConnListener) send(conn net.Conn) {
select {
case l.conn <- conn:
case <-l.done:
return
}
l.conn <- conn
close(l.conn)
}

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