Compare commits

...

71 Commits

Author SHA1 Message Date
wenyifan
3c1985e980 Merge branch 'refs/heads/master' into dev 2024-07-05 13:45:23 +08:00
ginuerzh
22537ff0d2 mv observer/stats to core 2024-07-04 23:05:35 +08:00
ginuerzh
b583e29a56 Bump github.com/go-gost/gosocks5 from 0.3.1 to 0.4.1 2024-07-04 09:34:35 +08:00
Wankko Ree
ba2a83a51d fix QUIC error "CRYPTO_ERROR 0x178 (remote): tls: no application protocol" 2024-07-03 22:06:17 +08:00
ginuerzh
74dc03bd66 fix http proxy response 2024-06-25 22:09:00 +08:00
ginuerzh
b99292bed8 add dialTimeout option for service 2024-06-25 20:40:38 +08:00
ginuerzh
f9bfca76ed fix netns for socks5 and relay handler 2024-06-24 21:18:04 +08:00
ginuerzh
2ae0462822 add support for linux network namespace 2024-06-21 23:38:18 +08:00
wenyifan
423dd1e35d Merge branch 'master' into dev
# Conflicts:
#	go.mod
#	go.sum
2024-06-19 10:10:55 +08:00
ginuerzh
15f28c667a fix wt dialer 2024-06-13 22:24:50 +08:00
ginuerzh
9bae597cbb Bump deps 2024-06-13 21:53:14 +08:00
ginuerzh
6d819a0c06 add observePeriod option for observer 2024-06-13 21:22:29 +08:00
ginuerzh
784e4b2b01 http: fix non-connect method request handler 2024-06-11 21:50:11 +08:00
ginuerzh
e793b2743b api: fix object name 2024-06-11 21:32:48 +08:00
ginuerzh
ce60160cd7 fix marker for selector 2024-06-07 20:57:47 +08:00
ginuerzh
118ee91c95 fix error response for web APIs 2024-06-04 22:05:13 +08:00
ginuerzh
754b2fdeac fix redis loader for hop 2024-05-08 21:26:15 +08:00
ginuerzh
40f709880d add node filter config 2024-05-08 21:25:14 +08:00
ginuerzh
332a3a1cd0 add p2p option for tun handler 2024-04-26 20:56:56 +08:00
ginuerzh
f2a5089c29 update api doc 2024-04-25 21:45:11 +08:00
ginuerzh
55058573d6 add sd and observer web APIs 2024-04-25 21:31:26 +08:00
ginuerzh
41b5e62207 added ohttps dialer 2024-04-25 14:59:01 +08:00
ginuerzh
766ce7fdaa merger origin/master 2024-04-25 09:44:49 +08:00
ginuerzh
254875cd30 update go.mod 2024-04-24 23:38:50 +08:00
dependabot[bot]
66104cd079 Bump golang.org/x/net from 0.19.0 to 0.23.0
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.19.0 to 0.23.0.
- [Commits](https://github.com/golang/net/compare/v0.19.0...v0.23.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-24 23:20:51 +08:00
ginuerzh
871afeeb6d
Merge pull request #27 from cgroschupp/feature/whitelist-change
feat: add whitelist support
2024-04-24 23:13:29 +08:00
ginuerzh
c3b133a2de
Merge pull request #26 from cgroschupp/feature/darwin_redirect
feat: add redirect darwin support
2024-04-24 23:09:07 +08:00
ginuerzh
68f9690494
Merge pull request #25 from cgroschupp/feature/homedir-expand-ssh
feat: expand homedir of ssh private key
2024-04-24 23:07:50 +08:00
ginuerzh
c35a79b2c9
Merge branch 'master' into feature/homedir-expand-ssh 2024-04-24 23:07:38 +08:00
ginuerzh
a2ab48c423
Merge pull request #24 from cgroschupp/feature/keyring-ssh-passphrase
feat: allow to read ssh passphrase from keyring
2024-04-24 22:46:00 +08:00
ginuerzh
902e24e7e8
Merge branch 'master' into feature/keyring-ssh-passphrase 2024-04-24 22:45:29 +08:00
dependabot[bot]
d26bf4f05c Bump github.com/quic-go/quic-go from 0.40.1 to 0.42.0
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.40.1 to 0.42.0.
- [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.40.1...v0.42.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-24 22:35:54 +08:00
dependabot[bot]
5ea88aa5cf Bump google.golang.org/protobuf from 1.31.0 to 1.33.0
Bumps google.golang.org/protobuf from 1.31.0 to 1.33.0.

---
updated-dependencies:
- dependency-name: google.golang.org/protobuf
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-04-24 22:26:00 +08:00
Christian Groschupp
77a8f28edc feat: add whitelist support 2024-04-16 21:06:14 +02:00
Christian Groschupp
7bf0537243 feat: add redirect darwin support 2024-04-16 20:56:21 +02:00
Christian Groschupp
6ba22b0935 feat: expand homedir of ssh private key 2024-04-16 16:43:47 +02:00
Christian Groschupp
7da8b2a710 feat: allow to read ssh passphrase from keyring 2024-04-16 16:42:50 +02:00
ginuerzh
d9b7585856 add more options for kcp dialer/listener 2024-03-09 20:58:22 +08:00
wenyifan
63ad7f2354 Merge branch 'master' into dev 2024-02-02 14:08:43 +08:00
ginuerzh
25dcf536c6 added url path rewrite for forwarder node 2024-01-31 23:18:42 +08:00
ginuerzh
3d2a7b7d3b move x/internal/ctx to x/ctx 2024-01-30 18:19:39 +08:00
wenyifan
5cc2c3de82 Merge branch 'master' into dev 2024-01-29 09:21:54 +08:00
ginuerzh
5ee7746aab fix tunnel connector selection 2024-01-28 18:56:19 +08:00
ginuerzh
3616a0d8a4 fix tunnel weight 2024-01-28 18:33:54 +08:00
ginuerzh
b5b39de62c add weight for tunnel connector 2024-01-27 23:31:23 +08:00
ginuerzh
43d37d0a5f fix hop http plugin 2024-01-27 21:29:45 +08:00
ginuerzh
8bdd7ee172 added auther option for node http settings 2024-01-27 21:29:45 +08:00
ginuerzh
a618998b36
Merge pull request #20 from go-gost/dependabot/go_modules/github.com/quic-go/quic-go-0.40.1
Bump github.com/quic-go/quic-go from 0.40.0 to 0.40.1
2024-01-18 17:42:34 +08:00
wenyifan
bb1a9908d4 Merge branch 'master' into dev 2024-01-18 09:24:27 +08:00
ginuerzh
01168e9846 fix deadlock in websocket client conn 2024-01-12 23:46:22 +08:00
dependabot[bot]
53aab11764
Bump github.com/quic-go/quic-go from 0.40.0 to 0.40.1
Bumps [github.com/quic-go/quic-go](https://github.com/quic-go/quic-go) from 0.40.0 to 0.40.1.
- [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.40.0...v0.40.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
2024-01-10 15:09:36 +00:00
ginuerzh
c04c28e1fd add protocol based node selection for hop 2024-01-08 21:22:20 +08:00
ginuerzh
936954ecf2 fix dns handler panic 2024-01-08 21:20:56 +08:00
ginuerzh
262ac0e9a5 add header for basic auth 2024-01-07 19:40:42 +08:00
ginuerzh
c959fc2f73 add observer 2024-01-03 20:55:06 +08:00
wenyifan
4e1a70ec6d Merge branch 'master' into dev 2023-12-28 17:33:48 +08:00
ginuerzh
e1ae379048 fix tls handshake sniffing 2023-12-27 19:24:31 +08:00
ginuerzh
1117723913 fix kcp option 2023-12-25 22:08:41 +08:00
wenyifan
590a6ae6ff Merge branch 'master' into dev 2023-12-21 17:47:19 +08:00
ginuerzh
9fa95cc8b3 bump github.com/spf13/viper 2023-12-20 21:57:29 +08:00
ginuerzh
40e9a8ce7b fix panic 2023-12-20 19:24:02 +08:00
ginuerzh
4a1b225d2c add logger group for service 2023-12-19 21:28:19 +08:00
ginuerzh
c4b95b180e
Merge pull request #19 from go-gost/dependabot/go_modules/golang.org/x/crypto-0.17.0
Bump golang.org/x/crypto from 0.16.0 to 0.17.0
2023-12-19 21:15:42 +08:00
dependabot[bot]
cc07ccb276
Bump golang.org/x/crypto from 0.16.0 to 0.17.0
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.16.0 to 0.17.0.
- [Commits](https://github.com/golang/crypto/compare/v0.16.0...v0.17.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
2023-12-19 00:02:48 +00:00
ginuerzh
f847fa533e fix auth for file handler 2023-12-16 14:28:58 +08:00
ginuerzh
b1390dda1c router: update system routes for linux 2023-11-30 19:39:37 +08:00
ginuerzh
8ef341dc88
Merge pull request #16 from gost-dev/bump-version
Bump deps
2023-11-30 16:19:22 +08:00
Charles Xu
94f8afdf45 Merge remote-tracking branch 'upstream/master' into bump-version 2023-11-30 00:31:50 +00:00
汪航洋
6ea815eb36 api allows private networking 2023-11-29 18:52:44 +08:00
Charles Xu
ee80eedac3 clean deps 2023-11-23 03:39:24 +00:00
Charles Xu
7cc1ef436f bump versions 2023-11-23 03:39:06 +00:00
196 changed files with 5393 additions and 2399 deletions

67
admission/plugin/grpc.go Normal file
View File

@ -0,0 +1,67 @@
package admission
import (
"context"
"io"
"github.com/go-gost/core/admission"
"github.com/go-gost/core/logger"
"github.com/go-gost/plugin/admission/proto"
"github.com/go-gost/x/internal/plugin"
"google.golang.org/grpc"
)
type grpcPlugin struct {
conn grpc.ClientConnInterface
client proto.AdmissionClient
log logger.Logger
}
// NewGRPCPlugin creates an Admission plugin based on gRPC.
func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) admission.Admission {
var options plugin.Options
for _, opt := range opts {
opt(&options)
}
log := logger.Default().WithFields(map[string]any{
"kind": "admission",
"admission": name,
})
conn, err := plugin.NewGRPCConn(addr, &options)
if err != nil {
log.Error(err)
}
p := &grpcPlugin{
conn: conn,
log: log,
}
if conn != nil {
p.client = proto.NewAdmissionClient(conn)
}
return p
}
func (p *grpcPlugin) Admit(ctx context.Context, addr string, opts ...admission.Option) bool {
if p.client == nil {
return false
}
r, err := p.client.Admit(ctx,
&proto.AdmissionRequest{
Addr: addr,
})
if err != nil {
p.log.Error(err)
return false
}
return r.Ok
}
func (p *grpcPlugin) Close() error {
if closer, ok := p.conn.(io.Closer); ok {
return closer.Close()
}
return nil
}

View File

@ -4,71 +4,13 @@ import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"github.com/go-gost/core/admission"
"github.com/go-gost/core/logger"
"github.com/go-gost/plugin/admission/proto"
"github.com/go-gost/x/internal/plugin"
"google.golang.org/grpc"
)
type grpcPlugin struct {
conn grpc.ClientConnInterface
client proto.AdmissionClient
log logger.Logger
}
// NewGRPCPlugin creates an Admission plugin based on gRPC.
func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) admission.Admission {
var options plugin.Options
for _, opt := range opts {
opt(&options)
}
log := logger.Default().WithFields(map[string]any{
"kind": "admission",
"admission": name,
})
conn, err := plugin.NewGRPCConn(addr, &options)
if err != nil {
log.Error(err)
}
p := &grpcPlugin{
conn: conn,
log: log,
}
if conn != nil {
p.client = proto.NewAdmissionClient(conn)
}
return p
}
func (p *grpcPlugin) Admit(ctx context.Context, addr string, opts ...admission.Option) bool {
if p.client == nil {
return false
}
r, err := p.client.Admit(ctx,
&proto.AdmissionRequest{
Addr: addr,
})
if err != nil {
p.log.Error(err)
return false
}
return r.Ok
}
func (p *grpcPlugin) Close() error {
if closer, ok := p.conn.(io.Closer); ok {
return closer.Close()
}
return nil
}
type httpPluginRequest struct {
Addr string `json:"addr"`
}

View File

@ -2,6 +2,13 @@ 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 (
@ -13,3 +20,172 @@ type Response struct {
Code int `json:"code,omitempty"`
Msg string `json:"msg,omitempty"`
}
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
}
var options options
for _, opt := range opts {
opt(&options)
}
gin.SetMode(gin.ReleaseMode)
r := gin.New()
r.Use(
cors.New((cors.Config{
AllowAllOrigins: true,
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"*"},
AllowPrivateNetwork: true,
})),
gin.Recovery(),
)
if options.accessLog {
r.Use(mwLogger())
}
router := r.Group("")
if options.pathPrefix != "" {
router = router.Group(options.pathPrefix)
}
router.StaticFS("/docs", http.FS(swaggerDoc))
config := router.Group("/config")
config.Use(mwBasicAuth(options.auther))
registerConfig(config)
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("/services", createService)
config.PUT("/services/:service", updateService)
config.DELETE("/services/:service", deleteService)
config.POST("/chains", createChain)
config.PUT("/chains/:chain", updateChain)
config.DELETE("/chains/:chain", deleteChain)
config.POST("/hops", createHop)
config.PUT("/hops/:hop", updateHop)
config.DELETE("/hops/:hop", deleteHop)
config.POST("/authers", createAuther)
config.PUT("/authers/:auther", updateAuther)
config.DELETE("/authers/:auther", deleteAuther)
config.POST("/admissions", createAdmission)
config.PUT("/admissions/:admission", updateAdmission)
config.DELETE("/admissions/:admission", deleteAdmission)
config.POST("/bypasses", createBypass)
config.PUT("/bypasses/:bypass", updateBypass)
config.DELETE("/bypasses/:bypass", deleteBypass)
config.POST("/resolvers", createResolver)
config.PUT("/resolvers/:resolver", updateResolver)
config.DELETE("/resolvers/:resolver", deleteResolver)
config.POST("/hosts", createHosts)
config.PUT("/hosts/:hosts", updateHosts)
config.DELETE("/hosts/:hosts", deleteHosts)
config.POST("/ingresses", createIngress)
config.PUT("/ingresses/:ingress", updateIngress)
config.DELETE("/ingresses/:ingress", deleteIngress)
config.POST("/routers", createRouter)
config.PUT("/routers/:router", updateRouter)
config.DELETE("/routers/:router", deleteRouter)
config.POST("/observers", createObserver)
config.PUT("/observers/:observer", updateObserver)
config.DELETE("/observers/:observer", deleteObserver)
config.POST("/recorders", createRecorder)
config.PUT("/recorders/:recorder", updateRecorder)
config.DELETE("/recorders/:recorder", deleteRecorder)
config.POST("/sds", createSD)
config.PUT("/sds/:sd", updateSD)
config.DELETE("/sds/:sd", deleteSD)
config.POST("/limiters", createLimiter)
config.PUT("/limiters/:limiter", updateLimiter)
config.DELETE("/limiters/:limiter", deleteLimiter)
config.POST("/climiters", createConnLimiter)
config.PUT("/climiters/:limiter", updateConnLimiter)
config.DELETE("/climiters/:limiter", deleteConnLimiter)
config.POST("/rlimiters", createRateLimiter)
config.PUT("/rlimiters/:limiter", updateRateLimiter)
config.DELETE("/rlimiters/:limiter", deleteRateLimiter)
}

View File

@ -7,9 +7,16 @@ import (
"os"
"github.com/gin-gonic/gin"
"github.com/go-gost/core/observer/stats"
"github.com/go-gost/x/config"
"github.com/go-gost/x/registry"
"github.com/go-gost/x/service"
)
type serviceStatus interface {
Status() *service.Status
}
// swagger:parameters getConfigRequest
type getConfigRequest struct {
// output format, one of yaml|json, default is json.
@ -37,6 +44,40 @@ func getConfig(ctx *gin.Context) {
var req getConfigRequest
ctx.ShouldBindQuery(&req)
config.OnUpdate(func(c *config.Config) error {
for _, svc := range c.Services {
if svc == nil {
continue
}
s := registry.ServiceRegistry().Get(svc.Name)
ss, ok := s.(serviceStatus)
if ok && ss != nil {
status := ss.Status()
svc.Status = &config.ServiceStatus{
CreateTime: status.CreateTime().Unix(),
State: string(status.State()),
}
if st := status.Stats(); st != nil {
svc.Status.Stats = &config.ServiceStats{
TotalConns: st.Get(stats.KindTotalConns),
CurrentConns: st.Get(stats.KindCurrentConns),
TotalErrs: st.Get(stats.KindTotalErrs),
InputBytes: st.Get(stats.KindInputBytes),
OutputBytes: st.Get(stats.KindOutputBytes),
}
}
for _, ev := range status.Events() {
if !ev.Time.IsZero() {
svc.Status.Events = append(svc.Status.Events, config.ServiceEvent{
Time: ev.Time.Unix(),
Msg: ev.Message,
})
}
}
}
}
return nil
})
var resp getConfigResponse
resp.Config = config.Global()
@ -103,7 +144,7 @@ func saveConfig(ctx *gin.Context) {
if err != nil {
writeError(ctx, &Error{
statusCode: http.StatusInternalServerError,
Code: 40005,
Code: ErrCodeSaveConfigFailed,
Msg: fmt.Sprintf("create file: %s", err.Error()),
})
return
@ -113,8 +154,8 @@ func saveConfig(ctx *gin.Context) {
if err := config.Global().Write(f, req.Format); err != nil {
writeError(ctx, &Error{
statusCode: http.StatusInternalServerError,
Code: 40006,
Msg: fmt.Sprintf("write: %s", err.Error()),
Code: ErrCodeSaveConfigFailed,
Msg: fmt.Sprintf("save config: %s", err.Error()),
})
return
}

View File

@ -1,7 +1,9 @@
package api
import (
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/go-gost/x/config"
@ -35,15 +37,22 @@ func createAdmission(ctx *gin.Context) {
var req createAdmissionRequest
ctx.ShouldBindJSON(&req.Data)
if req.Data.Name == "" {
writeError(ctx, ErrInvalid)
name := strings.TrimSpace(req.Data.Name)
if name == "" {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "admission name is required"))
return
}
req.Data.Name = name
if registry.AdmissionRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("admission %s already exists", name)))
return
}
v := parser.ParseAdmission(&req.Data)
if err := registry.AdmissionRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.AdmissionRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("admission %s already exists", name)))
return
}
@ -87,25 +96,27 @@ func updateAdmission(ctx *gin.Context) {
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.AdmissionRegistry().IsRegistered(req.Admission) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Admission)
if !registry.AdmissionRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("admission %s not found", name)))
return
}
req.Data.Name = req.Admission
req.Data.Name = name
v := parser.ParseAdmission(&req.Data)
registry.AdmissionRegistry().Unregister(req.Admission)
registry.AdmissionRegistry().Unregister(name)
if err := registry.AdmissionRegistry().Register(req.Admission, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.AdmissionRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("admission %s already exists", name)))
return
}
config.OnUpdate(func(c *config.Config) error {
for i := range c.Admissions {
if c.Admissions[i].Name == req.Admission {
if c.Admissions[i].Name == name {
c.Admissions[i] = &req.Data
break
}
@ -145,17 +156,19 @@ func deleteAdmission(ctx *gin.Context) {
var req deleteAdmissionRequest
ctx.ShouldBindUri(&req)
if !registry.AdmissionRegistry().IsRegistered(req.Admission) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Admission)
if !registry.AdmissionRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("admission %s not found", name)))
return
}
registry.AdmissionRegistry().Unregister(req.Admission)
registry.AdmissionRegistry().Unregister(name)
config.OnUpdate(func(c *config.Config) error {
admissiones := c.Admissions
c.Admissions = nil
for _, s := range admissiones {
if s.Name == req.Admission {
if s.Name == name {
continue
}
c.Admissions = append(c.Admissions, s)

View File

@ -1,7 +1,9 @@
package api
import (
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/go-gost/x/config"
@ -35,14 +37,21 @@ func createAuther(ctx *gin.Context) {
var req createAutherRequest
ctx.ShouldBindJSON(&req.Data)
if req.Data.Name == "" {
writeError(ctx, ErrInvalid)
name := strings.TrimSpace(req.Data.Name)
if name == "" {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "auther name is required"))
return
}
req.Data.Name = name
if registry.AutherRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("auther %s already exists", name)))
return
}
v := parser.ParseAuther(&req.Data)
if err := registry.AutherRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.AutherRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("auther %s already exists", name)))
return
}
@ -86,24 +95,26 @@ func updateAuther(ctx *gin.Context) {
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.AutherRegistry().IsRegistered(req.Auther) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Auther)
if !registry.AutherRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("auther %s not found", name)))
return
}
req.Data.Name = req.Auther
req.Data.Name = name
v := parser.ParseAuther(&req.Data)
registry.AutherRegistry().Unregister(req.Auther)
registry.AutherRegistry().Unregister(name)
if err := registry.AutherRegistry().Register(req.Auther, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.AutherRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("auther %s already exists", name)))
return
}
config.OnUpdate(func(c *config.Config) error {
for i := range c.Authers {
if c.Authers[i].Name == req.Auther {
if c.Authers[i].Name == name {
c.Authers[i] = &req.Data
break
}
@ -143,17 +154,19 @@ func deleteAuther(ctx *gin.Context) {
var req deleteAutherRequest
ctx.ShouldBindUri(&req)
if !registry.AutherRegistry().IsRegistered(req.Auther) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Auther)
if !registry.AutherRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("auther %s not found", name)))
return
}
registry.AutherRegistry().Unregister(req.Auther)
registry.AutherRegistry().Unregister(name)
config.OnUpdate(func(c *config.Config) error {
authers := c.Authers
c.Authers = nil
for _, s := range authers {
if s.Name == req.Auther {
if s.Name == name {
continue
}
c.Authers = append(c.Authers, s)

View File

@ -1,7 +1,9 @@
package api
import (
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/go-gost/x/config"
@ -35,15 +37,22 @@ func createBypass(ctx *gin.Context) {
var req createBypassRequest
ctx.ShouldBindJSON(&req.Data)
if req.Data.Name == "" {
writeError(ctx, ErrInvalid)
name := strings.TrimSpace(req.Data.Name)
if name == "" {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "bypass name is required"))
return
}
req.Data.Name = name
if registry.BypassRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("bypass %s already exists", name)))
return
}
v := parser.ParseBypass(&req.Data)
if err := registry.BypassRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.BypassRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("bypass %s already exists", name)))
return
}
@ -87,25 +96,27 @@ func updateBypass(ctx *gin.Context) {
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.BypassRegistry().IsRegistered(req.Bypass) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Bypass)
if !registry.BypassRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("bypass %s not found", name)))
return
}
req.Data.Name = req.Bypass
req.Data.Name = name
v := parser.ParseBypass(&req.Data)
registry.BypassRegistry().Unregister(req.Bypass)
registry.BypassRegistry().Unregister(name)
if err := registry.BypassRegistry().Register(req.Bypass, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.BypassRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("bypass %s already exists", name)))
return
}
config.OnUpdate(func(c *config.Config) error {
for i := range c.Bypasses {
if c.Bypasses[i].Name == req.Bypass {
if c.Bypasses[i].Name == name {
c.Bypasses[i] = &req.Data
break
}
@ -145,17 +156,19 @@ func deleteBypass(ctx *gin.Context) {
var req deleteBypassRequest
ctx.ShouldBindUri(&req)
if !registry.BypassRegistry().IsRegistered(req.Bypass) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Bypass)
if !registry.BypassRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("bypass %s not found", name)))
return
}
registry.BypassRegistry().Unregister(req.Bypass)
registry.BypassRegistry().Unregister(name)
config.OnUpdate(func(c *config.Config) error {
bypasses := c.Bypasses
c.Bypasses = nil
for _, s := range bypasses {
if s.Name == req.Bypass {
if s.Name == name {
continue
}
c.Bypasses = append(c.Bypasses, s)

View File

@ -1,9 +1,12 @@
package api
import (
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/go-gost/core/logger"
"github.com/go-gost/x/config"
parser "github.com/go-gost/x/config/parsing/chain"
"github.com/go-gost/x/registry"
@ -35,19 +38,26 @@ func createChain(ctx *gin.Context) {
var req createChainRequest
ctx.ShouldBindJSON(&req.Data)
if req.Data.Name == "" {
writeError(ctx, ErrInvalid)
name := strings.TrimSpace(req.Data.Name)
if name == "" {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "chain name is required"))
return
}
req.Data.Name = name
if registry.ChainRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("chain %s already exists", name)))
return
}
v, err := parser.ParseChain(&req.Data)
v, err := parser.ParseChain(&req.Data, logger.Default())
if err != nil {
writeError(ctx, ErrCreate)
writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create chain %s failed: %s", name, err.Error())))
return
}
if err := registry.ChainRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.ChainRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("chain %s already exists", name)))
return
}
@ -92,29 +102,31 @@ func updateChain(ctx *gin.Context) {
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.ChainRegistry().IsRegistered(req.Chain) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Chain)
if !registry.ChainRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("chain %s not found", name)))
return
}
req.Data.Name = req.Chain
req.Data.Name = name
v, err := parser.ParseChain(&req.Data)
v, err := parser.ParseChain(&req.Data, logger.Default())
if err != nil {
writeError(ctx, ErrCreate)
writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create chain %s failed: %s", name, err.Error())))
return
}
registry.ChainRegistry().Unregister(req.Chain)
registry.ChainRegistry().Unregister(name)
if err := registry.ChainRegistry().Register(req.Chain, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.ChainRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("chain %s already exists", name)))
return
}
config.OnUpdate(func(c *config.Config) error {
for i := range c.Chains {
if c.Chains[i].Name == req.Chain {
if c.Chains[i].Name == name {
c.Chains[i] = &req.Data
break
}
@ -154,17 +166,19 @@ func deleteChain(ctx *gin.Context) {
var req deleteChainRequest
ctx.ShouldBindUri(&req)
if !registry.ChainRegistry().IsRegistered(req.Chain) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Chain)
if !registry.ChainRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("chain %s not found", name)))
return
}
registry.ChainRegistry().Unregister(req.Chain)
registry.ChainRegistry().Unregister(name)
config.OnUpdate(func(c *config.Config) error {
chains := c.Chains
c.Chains = nil
for _, s := range chains {
if s.Name == req.Chain {
if s.Name == name {
continue
}
c.Chains = append(c.Chains, s)

View File

@ -1,7 +1,9 @@
package api
import (
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/go-gost/x/config"
@ -35,15 +37,21 @@ func createConnLimiter(ctx *gin.Context) {
var req createConnLimiterRequest
ctx.ShouldBindJSON(&req.Data)
if req.Data.Name == "" {
writeError(ctx, ErrInvalid)
name := strings.TrimSpace(req.Data.Name)
if name == "" {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "limiter name is required"))
return
}
req.Data.Name = name
if registry.ConnLimiterRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name)))
return
}
v := parser.ParseConnLimiter(&req.Data)
if err := registry.ConnLimiterRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.ConnLimiterRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create limmiter %s failed: %s", name, err.Error())))
return
}
@ -87,25 +95,27 @@ func updateConnLimiter(ctx *gin.Context) {
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.ConnLimiterRegistry().IsRegistered(req.Limiter) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Limiter)
if !registry.ConnLimiterRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("limiter %s not found", name)))
return
}
req.Data.Name = req.Limiter
req.Data.Name = name
v := parser.ParseConnLimiter(&req.Data)
registry.ConnLimiterRegistry().Unregister(req.Limiter)
registry.ConnLimiterRegistry().Unregister(name)
if err := registry.ConnLimiterRegistry().Register(req.Limiter, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.ConnLimiterRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name)))
return
}
config.OnUpdate(func(c *config.Config) error {
for i := range c.CLimiters {
if c.CLimiters[i].Name == req.Limiter {
if c.CLimiters[i].Name == name {
c.CLimiters[i] = &req.Data
break
}
@ -145,17 +155,19 @@ func deleteConnLimiter(ctx *gin.Context) {
var req deleteConnLimiterRequest
ctx.ShouldBindUri(&req)
if !registry.ConnLimiterRegistry().IsRegistered(req.Limiter) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Limiter)
if !registry.ConnLimiterRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("limiter %s not found", name)))
return
}
registry.ConnLimiterRegistry().Unregister(req.Limiter)
registry.ConnLimiterRegistry().Unregister(name)
config.OnUpdate(func(c *config.Config) error {
limiteres := c.CLimiters
c.CLimiters = nil
for _, s := range limiteres {
if s.Name == req.Limiter {
if s.Name == name {
continue
}
c.CLimiters = append(c.CLimiters, s)

View File

@ -1,9 +1,12 @@
package api
import (
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/go-gost/core/logger"
"github.com/go-gost/x/config"
parser "github.com/go-gost/x/config/parsing/hop"
"github.com/go-gost/x/registry"
@ -35,19 +38,26 @@ func createHop(ctx *gin.Context) {
var req createHopRequest
ctx.ShouldBindJSON(&req.Data)
if req.Data.Name == "" {
writeError(ctx, ErrInvalid)
name := strings.TrimSpace(req.Data.Name)
if name == "" {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "hop name is required"))
return
}
req.Data.Name = name
if registry.HopRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("hop %s already exists", name)))
return
}
v, err := parser.ParseHop(&req.Data)
v, err := parser.ParseHop(&req.Data, logger.Default())
if err != nil {
writeError(ctx, ErrCreate)
writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create hop %s failed: %s", name, err.Error())))
return
}
if err := registry.HopRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.HopRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("hop %s already exists", name)))
return
}
@ -92,29 +102,30 @@ func updateHop(ctx *gin.Context) {
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.HopRegistry().IsRegistered(req.Hop) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Hop)
if !registry.HopRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("hop %s not found", name)))
return
}
req.Data.Name = req.Hop
req.Data.Name = name
v, err := parser.ParseHop(&req.Data)
v, err := parser.ParseHop(&req.Data, logger.Default())
if err != nil {
writeError(ctx, ErrCreate)
writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create hop %s failed: %s", name, err.Error())))
return
}
registry.HopRegistry().Unregister(req.Hop)
registry.HopRegistry().Unregister(name)
if err := registry.HopRegistry().Register(req.Hop, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.HopRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("hop %s already exists", name)))
return
}
config.OnUpdate(func(c *config.Config) error {
for i := range c.Hops {
if c.Hops[i].Name == req.Hop {
if c.Hops[i].Name == name {
c.Hops[i] = &req.Data
break
}
@ -154,17 +165,19 @@ func deleteHop(ctx *gin.Context) {
var req deleteHopRequest
ctx.ShouldBindUri(&req)
if !registry.HopRegistry().IsRegistered(req.Hop) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Hop)
if !registry.HopRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("hop %s not found", name)))
return
}
registry.HopRegistry().Unregister(req.Hop)
registry.HopRegistry().Unregister(name)
config.OnUpdate(func(c *config.Config) error {
hops := c.Hops
c.Hops = nil
for _, s := range hops {
if s.Name == req.Hop {
if s.Name == name {
continue
}
c.Hops = append(c.Hops, s)

View File

@ -1,7 +1,9 @@
package api
import (
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/go-gost/x/config"
@ -35,15 +37,22 @@ func createHosts(ctx *gin.Context) {
var req createHostsRequest
ctx.ShouldBindJSON(&req.Data)
if req.Data.Name == "" {
writeError(ctx, ErrInvalid)
name := strings.TrimSpace(req.Data.Name)
if name == "" {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "hosts name is required"))
return
}
req.Data.Name = name
if registry.HostsRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("hosts %s already exists", name)))
return
}
v := parser.ParseHostMapper(&req.Data)
if err := registry.HostsRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.HostsRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("hosts %s already exists", name)))
return
}
@ -87,25 +96,27 @@ func updateHosts(ctx *gin.Context) {
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.HostsRegistry().IsRegistered(req.Hosts) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Hosts)
if !registry.HostsRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("hosts %s not found", name)))
return
}
req.Data.Name = req.Hosts
req.Data.Name = name
v := parser.ParseHostMapper(&req.Data)
registry.HostsRegistry().Unregister(req.Hosts)
registry.HostsRegistry().Unregister(name)
if err := registry.HostsRegistry().Register(req.Hosts, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.HostsRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("hosts %s already exists", name)))
return
}
config.OnUpdate(func(c *config.Config) error {
for i := range c.Hosts {
if c.Hosts[i].Name == req.Hosts {
if c.Hosts[i].Name == name {
c.Hosts[i] = &req.Data
break
}
@ -145,17 +156,19 @@ func deleteHosts(ctx *gin.Context) {
var req deleteHostsRequest
ctx.ShouldBindUri(&req)
if !registry.HostsRegistry().IsRegistered(req.Hosts) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Hosts)
if !registry.HostsRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("hosts %s not found", name)))
return
}
registry.HostsRegistry().Unregister(req.Hosts)
registry.HostsRegistry().Unregister(name)
config.OnUpdate(func(c *config.Config) error {
hosts := c.Hosts
c.Hosts = nil
for _, s := range hosts {
if s.Name == req.Hosts {
if s.Name == name {
continue
}
c.Hosts = append(c.Hosts, s)

View File

@ -1,7 +1,9 @@
package api
import (
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/go-gost/x/config"
@ -35,15 +37,22 @@ func createIngress(ctx *gin.Context) {
var req createIngressRequest
ctx.ShouldBindJSON(&req.Data)
if req.Data.Name == "" {
writeError(ctx, ErrInvalid)
name := strings.TrimSpace(req.Data.Name)
if name == "" {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "ingress name is required"))
return
}
req.Data.Name = name
if registry.IngressRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("ingress %s already exists", name)))
return
}
v := parser.ParseIngress(&req.Data)
if err := registry.IngressRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.IngressRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("ingress %s already exists", name)))
return
}
@ -87,25 +96,27 @@ func updateIngress(ctx *gin.Context) {
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.IngressRegistry().IsRegistered(req.Ingress) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Ingress)
if !registry.IngressRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("ingress %s not found", name)))
return
}
req.Data.Name = req.Ingress
req.Data.Name = name
v := parser.ParseIngress(&req.Data)
registry.IngressRegistry().Unregister(req.Ingress)
registry.IngressRegistry().Unregister(name)
if err := registry.IngressRegistry().Register(req.Ingress, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.IngressRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("ingress %s already exists", name)))
return
}
config.OnUpdate(func(c *config.Config) error {
for i := range c.Ingresses {
if c.Ingresses[i].Name == req.Ingress {
if c.Ingresses[i].Name == name {
c.Ingresses[i] = &req.Data
break
}
@ -145,17 +156,19 @@ func deleteIngress(ctx *gin.Context) {
var req deleteIngressRequest
ctx.ShouldBindUri(&req)
if !registry.IngressRegistry().IsRegistered(req.Ingress) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Ingress)
if !registry.IngressRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("ingress %s not found", name)))
return
}
registry.IngressRegistry().Unregister(req.Ingress)
registry.IngressRegistry().Unregister(name)
config.OnUpdate(func(c *config.Config) error {
ingresses := c.Ingresses
c.Ingresses = nil
for _, s := range ingresses {
if s.Name == req.Ingress {
if s.Name == name {
continue
}
c.Ingresses = append(c.Ingresses, s)

View File

@ -1,7 +1,9 @@
package api
import (
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/go-gost/x/config"
@ -35,15 +37,22 @@ func createLimiter(ctx *gin.Context) {
var req createLimiterRequest
ctx.ShouldBindJSON(&req.Data)
if req.Data.Name == "" {
writeError(ctx, ErrInvalid)
name := strings.TrimSpace(req.Data.Name)
if name == "" {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "limiter name is required"))
return
}
req.Data.Name = name
if registry.TrafficLimiterRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name)))
return
}
v := parser.ParseTrafficLimiter(&req.Data)
if err := registry.TrafficLimiterRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.TrafficLimiterRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name)))
return
}
@ -87,25 +96,27 @@ func updateLimiter(ctx *gin.Context) {
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.TrafficLimiterRegistry().IsRegistered(req.Limiter) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Limiter)
if !registry.TrafficLimiterRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("limiter %s not found", name)))
return
}
req.Data.Name = req.Limiter
req.Data.Name = name
v := parser.ParseTrafficLimiter(&req.Data)
registry.TrafficLimiterRegistry().Unregister(req.Limiter)
registry.TrafficLimiterRegistry().Unregister(name)
if err := registry.TrafficLimiterRegistry().Register(req.Limiter, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.TrafficLimiterRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name)))
return
}
config.OnUpdate(func(c *config.Config) error {
for i := range c.Limiters {
if c.Limiters[i].Name == req.Limiter {
if c.Limiters[i].Name == name {
c.Limiters[i] = &req.Data
break
}
@ -145,17 +156,19 @@ func deleteLimiter(ctx *gin.Context) {
var req deleteLimiterRequest
ctx.ShouldBindUri(&req)
if !registry.TrafficLimiterRegistry().IsRegistered(req.Limiter) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Limiter)
if !registry.TrafficLimiterRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("limiter %s not found", name)))
return
}
registry.TrafficLimiterRegistry().Unregister(req.Limiter)
registry.TrafficLimiterRegistry().Unregister(name)
config.OnUpdate(func(c *config.Config) error {
limiteres := c.Limiters
c.Limiters = nil
for _, s := range limiteres {
if s.Name == req.Limiter {
if s.Name == name {
continue
}
c.Limiters = append(c.Limiters, s)

182
api/config_observer.go Normal file
View File

@ -0,0 +1,182 @@
package api
import (
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/go-gost/x/config"
parser "github.com/go-gost/x/config/parsing/observer"
"github.com/go-gost/x/registry"
)
// swagger:parameters createObserverRequest
type createObserverRequest struct {
// in: body
Data config.ObserverConfig `json:"data"`
}
// successful operation.
// swagger:response createObserverResponse
type createObserverResponse struct {
Data Response
}
func createObserver(ctx *gin.Context) {
// swagger:route POST /config/observers Observer createObserverRequest
//
// Create a new observer, the name of the observer must be unique in observer list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: createObserverResponse
var req createObserverRequest
ctx.ShouldBindJSON(&req.Data)
name := strings.TrimSpace(req.Data.Name)
if name == "" {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "observer name is required"))
return
}
req.Data.Name = name
if registry.ObserverRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("observer %s already exists", name)))
return
}
v := parser.ParseObserver(&req.Data)
if err := registry.ObserverRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("observer %s already exists", name)))
return
}
config.OnUpdate(func(c *config.Config) error {
c.Observers = append(c.Observers, &req.Data)
return nil
})
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters updateObserverRequest
type updateObserverRequest struct {
// in: path
// required: true
Observer string `uri:"observer" json:"observer"`
// in: body
Data config.ObserverConfig `json:"data"`
}
// successful operation.
// swagger:response updateObserverResponse
type updateObserverResponse struct {
Data Response
}
func updateObserver(ctx *gin.Context) {
// swagger:route PUT /config/observers/{observer} Observer updateObserverRequest
//
// Update observer by name, the observer must already exist.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: updateObserverResponse
var req updateObserverRequest
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
name := strings.TrimSpace(req.Observer)
if !registry.ObserverRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("observer %s not found", name)))
return
}
req.Data.Name = name
v := parser.ParseObserver(&req.Data)
registry.ObserverRegistry().Unregister(name)
if err := registry.ObserverRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("observer %s already exists", name)))
return
}
config.OnUpdate(func(c *config.Config) error {
for i := range c.Observers {
if c.Observers[i].Name == name {
c.Observers[i] = &req.Data
break
}
}
return nil
})
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters deleteObserverRequest
type deleteObserverRequest struct {
// in: path
// required: true
Observer string `uri:"observer" json:"observer"`
}
// successful operation.
// swagger:response deleteObserverResponse
type deleteObserverResponse struct {
Data Response
}
func deleteObserver(ctx *gin.Context) {
// swagger:route DELETE /config/observers/{observer} Observer deleteObserverRequest
//
// Delete observer by name.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: deleteObserverResponse
var req deleteObserverRequest
ctx.ShouldBindUri(&req)
name := strings.TrimSpace(req.Observer)
if !registry.ObserverRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("observer %s not found", name)))
return
}
registry.ObserverRegistry().Unregister(name)
config.OnUpdate(func(c *config.Config) error {
observers := c.Observers
c.Observers = nil
for _, s := range observers {
if s.Name == name {
continue
}
c.Observers = append(c.Observers, s)
}
return nil
})
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}

View File

@ -1,7 +1,9 @@
package api
import (
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/go-gost/x/config"
@ -35,15 +37,22 @@ func createRateLimiter(ctx *gin.Context) {
var req createRateLimiterRequest
ctx.ShouldBindJSON(&req.Data)
if req.Data.Name == "" {
writeError(ctx, ErrInvalid)
name := strings.TrimSpace(req.Data.Name)
if name == "" {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "limiter name is required"))
return
}
req.Data.Name = name
if registry.RateLimiterRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name)))
return
}
v := parser.ParseRateLimiter(&req.Data)
if err := registry.RateLimiterRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.RateLimiterRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name)))
return
}
@ -87,25 +96,27 @@ func updateRateLimiter(ctx *gin.Context) {
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.RateLimiterRegistry().IsRegistered(req.Limiter) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Limiter)
if !registry.RateLimiterRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("limiter %s not found", name)))
return
}
req.Data.Name = req.Limiter
req.Data.Name = name
v := parser.ParseRateLimiter(&req.Data)
registry.RateLimiterRegistry().Unregister(req.Limiter)
registry.RateLimiterRegistry().Unregister(name)
if err := registry.RateLimiterRegistry().Register(req.Limiter, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.RateLimiterRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name)))
return
}
config.OnUpdate(func(c *config.Config) error {
for i := range c.RLimiters {
if c.RLimiters[i].Name == req.Limiter {
if c.RLimiters[i].Name == name {
c.RLimiters[i] = &req.Data
break
}
@ -145,17 +156,19 @@ func deleteRateLimiter(ctx *gin.Context) {
var req deleteRateLimiterRequest
ctx.ShouldBindUri(&req)
if !registry.RateLimiterRegistry().IsRegistered(req.Limiter) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Limiter)
if !registry.RateLimiterRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("limiter %s not found", name)))
return
}
registry.RateLimiterRegistry().Unregister(req.Limiter)
registry.RateLimiterRegistry().Unregister(name)
config.OnUpdate(func(c *config.Config) error {
limiteres := c.RLimiters
c.RLimiters = nil
for _, s := range limiteres {
if s.Name == req.Limiter {
if s.Name == name {
continue
}
c.RLimiters = append(c.RLimiters, s)

181
api/config_recorder.go Normal file
View File

@ -0,0 +1,181 @@
package api
import (
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/go-gost/x/config"
parser "github.com/go-gost/x/config/parsing/recorder"
"github.com/go-gost/x/registry"
)
// swagger:parameters createRecorderRequest
type createRecorderRequest struct {
// in: body
Data config.RecorderConfig `json:"data"`
}
// successful operation.
// swagger:response createRecorderResponse
type createRecorderResponse struct {
Data Response
}
func createRecorder(ctx *gin.Context) {
// swagger:route POST /config/recorders Recorder createRecorderRequest
//
// Create a new recorder, the name of the recorder must be unique in recorder list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: createRecorderResponse
var req createRecorderRequest
ctx.ShouldBindJSON(&req.Data)
name := strings.TrimSpace(req.Data.Name)
if name == "" {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "recorder name is required"))
return
}
req.Data.Name = name
if registry.RecorderRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("recorder %s already exists", name)))
return
}
v := parser.ParseRecorder(&req.Data)
if err := registry.RecorderRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("recorder %s already exists", name)))
return
}
config.OnUpdate(func(c *config.Config) error {
c.Recorders = append(c.Recorders, &req.Data)
return nil
})
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters updateRecorderRequest
type updateRecorderRequest struct {
// in: path
// required: true
Recorder string `uri:"recorder" json:"recorder"`
// in: body
Data config.RecorderConfig `json:"data"`
}
// successful operation.
// swagger:response updateRecorderResponse
type updateRecorderResponse struct {
Data Response
}
func updateRecorder(ctx *gin.Context) {
// swagger:route PUT /config/recorders/{recorder} Recorder updateRecorderRequest
//
// Update recorder by name, the recorder must already exist.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: updateRecorderResponse
var req updateRecorderRequest
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
name := strings.TrimSpace(req.Recorder)
if !registry.RecorderRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("recorder %s not found", name)))
return
}
req.Data.Name = name
v := parser.ParseRecorder(&req.Data)
registry.RecorderRegistry().Unregister(name)
if err := registry.RecorderRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("recorder %s already exists", name)))
return
}
config.OnUpdate(func(c *config.Config) error {
for i := range c.Recorders {
if c.Recorders[i].Name == name {
c.Recorders[i] = &req.Data
break
}
}
return nil
})
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters deleteRecorderRequest
type deleteRecorderRequest struct {
// in: path
// required: true
Recorder string `uri:"recorder" json:"recorder"`
}
// successful operation.
// swagger:response deleteRecorderResponse
type deleteRecorderResponse struct {
Data Response
}
func deleteRecorder(ctx *gin.Context) {
// swagger:route DELETE /config/recorders/{recorder} Recorder deleteRecorderRequest
//
// Delete recorder by name.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: deleteRecorderResponse
var req deleteRecorderRequest
ctx.ShouldBindUri(&req)
name := strings.TrimSpace(req.Recorder)
if !registry.RecorderRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("recorder %s not found", name)))
return
}
registry.RecorderRegistry().Unregister(name)
config.OnUpdate(func(c *config.Config) error {
recorders := c.Recorders
c.Recorders = nil
for _, s := range recorders {
if s.Name == name {
continue
}
c.Recorders = append(c.Recorders, s)
}
return nil
})
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}

View File

@ -1,7 +1,9 @@
package api
import (
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/go-gost/x/config"
@ -35,19 +37,26 @@ func createResolver(ctx *gin.Context) {
var req createResolverRequest
ctx.ShouldBindJSON(&req.Data)
if req.Data.Name == "" {
writeError(ctx, ErrInvalid)
name := strings.TrimSpace(req.Data.Name)
if name == "" {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "resolver name is required"))
return
}
req.Data.Name = name
if registry.ResolverRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("resolver %s already exists", name)))
return
}
v, err := parser.ParseResolver(&req.Data)
if err != nil {
writeError(ctx, ErrCreate)
writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create resolver %s failed: %s", name, err.Error())))
return
}
if err := registry.ResolverRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.ResolverRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("resolver %s already exists", name)))
return
}
@ -91,29 +100,31 @@ func updateResolver(ctx *gin.Context) {
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.ResolverRegistry().IsRegistered(req.Resolver) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Resolver)
if !registry.ResolverRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("resolver %s not found", name)))
return
}
req.Data.Name = req.Resolver
req.Data.Name = name
v, err := parser.ParseResolver(&req.Data)
if err != nil {
writeError(ctx, ErrCreate)
writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create resolver %s failed: %s", name, err.Error())))
return
}
registry.ResolverRegistry().Unregister(req.Resolver)
registry.ResolverRegistry().Unregister(name)
if err := registry.ResolverRegistry().Register(req.Resolver, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.ResolverRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("resolver %s already exists", name)))
return
}
config.OnUpdate(func(c *config.Config) error {
for i := range c.Resolvers {
if c.Resolvers[i].Name == req.Resolver {
if c.Resolvers[i].Name == name {
c.Resolvers[i] = &req.Data
break
}
@ -153,17 +164,19 @@ func deleteResolver(ctx *gin.Context) {
var req deleteResolverRequest
ctx.ShouldBindUri(&req)
if !registry.ResolverRegistry().IsRegistered(req.Resolver) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Resolver)
if !registry.ResolverRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("resolver %s not found", name)))
return
}
registry.ResolverRegistry().Unregister(req.Resolver)
registry.ResolverRegistry().Unregister(name)
config.OnUpdate(func(c *config.Config) error {
resolvers := c.Resolvers
c.Resolvers = nil
for _, s := range resolvers {
if s.Name == req.Resolver {
if s.Name == name {
continue
}
c.Resolvers = append(c.Resolvers, s)

View File

@ -1,7 +1,9 @@
package api
import (
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/go-gost/x/config"
@ -35,15 +37,22 @@ func createRouter(ctx *gin.Context) {
var req createRouterRequest
ctx.ShouldBindJSON(&req.Data)
if req.Data.Name == "" {
writeError(ctx, ErrInvalid)
name := strings.TrimSpace(req.Data.Name)
if name == "" {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "router name is required"))
return
}
req.Data.Name = name
if registry.RouterRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("router %s already exists", name)))
return
}
v := parser.ParseRouter(&req.Data)
if err := registry.RouterRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.RouterRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("router %s already exists", name)))
return
}
@ -87,25 +96,27 @@ func updateRouter(ctx *gin.Context) {
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.RouterRegistry().IsRegistered(req.Router) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Router)
if !registry.RouterRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("router %s not found", name)))
return
}
req.Data.Name = req.Router
req.Data.Name = name
v := parser.ParseRouter(&req.Data)
registry.RouterRegistry().Unregister(req.Router)
registry.RouterRegistry().Unregister(name)
if err := registry.RouterRegistry().Register(req.Router, v); err != nil {
writeError(ctx, ErrDup)
if err := registry.RouterRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("router %s already exists", name)))
return
}
config.OnUpdate(func(c *config.Config) error {
for i := range c.Routers {
if c.Routers[i].Name == req.Router {
if c.Routers[i].Name == name {
c.Routers[i] = &req.Data
break
}
@ -145,17 +156,19 @@ func deleteRouter(ctx *gin.Context) {
var req deleteRouterRequest
ctx.ShouldBindUri(&req)
if !registry.RouterRegistry().IsRegistered(req.Router) {
writeError(ctx, ErrNotFound)
name := strings.TrimSpace(req.Router)
if !registry.RouterRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("router %s not found", name)))
return
}
registry.RouterRegistry().Unregister(req.Router)
registry.RouterRegistry().Unregister(name)
config.OnUpdate(func(c *config.Config) error {
routeres := c.Routers
c.Routers = nil
for _, s := range routeres {
if s.Name == req.Router {
if s.Name == name {
continue
}
c.Routers = append(c.Routers, s)

182
api/config_sd.go Normal file
View File

@ -0,0 +1,182 @@
package api
import (
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/go-gost/x/config"
parser "github.com/go-gost/x/config/parsing/sd"
"github.com/go-gost/x/registry"
)
// swagger:parameters createSDRequest
type createSDRequest struct {
// in: body
Data config.SDConfig `json:"data"`
}
// successful operation.
// swagger:response createSDResponse
type createSDResponse struct {
Data Response
}
func createSD(ctx *gin.Context) {
// swagger:route POST /config/sds SD createSDRequest
//
// Create a new SD, the name of the SD must be unique in SD list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: createSDResponse
var req createSDRequest
ctx.ShouldBindJSON(&req.Data)
name := strings.TrimSpace(req.Data.Name)
if name == "" {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "sd name is required"))
return
}
req.Data.Name = name
if registry.SDRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("sd %s already exists", name)))
return
}
v := parser.ParseSD(&req.Data)
if err := registry.SDRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("sd %s already exists", name)))
return
}
config.OnUpdate(func(c *config.Config) error {
c.SDs = append(c.SDs, &req.Data)
return nil
})
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters updateSDRequest
type updateSDRequest struct {
// in: path
// required: true
SD string `uri:"sd" json:"sd"`
// in: body
Data config.SDConfig `json:"data"`
}
// successful operation.
// swagger:response updateSDResponse
type updateSDResponse struct {
Data Response
}
func updateSD(ctx *gin.Context) {
// swagger:route PUT /config/sds/{sd} SD updateSDRequest
//
// Update SD by name, the SD must already exist.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: updateSDResponse
var req updateSDRequest
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
name := strings.TrimSpace(req.SD)
if !registry.SDRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("sd %s not found", name)))
return
}
req.Data.Name = name
v := parser.ParseSD(&req.Data)
registry.SDRegistry().Unregister(name)
if err := registry.SDRegistry().Register(name, v); err != nil {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("sd %s already exists", name)))
return
}
config.OnUpdate(func(c *config.Config) error {
for i := range c.SDs {
if c.SDs[i].Name == name {
c.SDs[i] = &req.Data
break
}
}
return nil
})
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters deleteSDRequest
type deleteSDRequest struct {
// in: path
// required: true
SD string `uri:"sd" json:"sd"`
}
// successful operation.
// swagger:response deleteSDResponse
type deleteSDResponse struct {
Data Response
}
func deleteSD(ctx *gin.Context) {
// swagger:route DELETE /config/sds/{sd} SD deleteSDRequest
//
// Delete SD by name.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: deleteSDResponse
var req deleteSDRequest
ctx.ShouldBindUri(&req)
name := strings.TrimSpace(req.SD)
if !registry.SDRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("sd %s not found", name)))
return
}
registry.SDRegistry().Unregister(name)
config.OnUpdate(func(c *config.Config) error {
sds := c.SDs
c.SDs = nil
for _, s := range sds {
if s.Name == name {
continue
}
c.SDs = append(c.SDs, s)
}
return nil
})
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}

View File

@ -1,7 +1,9 @@
package api
import (
"fmt"
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/go-gost/x/config"
@ -35,25 +37,27 @@ func createService(ctx *gin.Context) {
var req createServiceRequest
ctx.ShouldBindJSON(&req.Data)
if req.Data.Name == "" {
writeError(ctx, ErrInvalid)
name := strings.TrimSpace(req.Data.Name)
if name == "" {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeInvalid, "service name is required"))
return
}
req.Data.Name = name
if registry.ServiceRegistry().IsRegistered(req.Data.Name) {
writeError(ctx, ErrDup)
if registry.ServiceRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("service %s already exists", name)))
return
}
svc, err := parser.ParseService(&req.Data)
if err != nil {
writeError(ctx, ErrCreate)
writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create service %s failed: %s", name, err.Error())))
return
}
if err := registry.ServiceRegistry().Register(req.Data.Name, svc); err != nil {
if err := registry.ServiceRegistry().Register(name, svc); err != nil {
svc.Close()
writeError(ctx, ErrDup)
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("service %s already exists", name)))
return
}
@ -99,26 +103,28 @@ func updateService(ctx *gin.Context) {
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
old := registry.ServiceRegistry().Get(req.Service)
name := strings.TrimSpace(req.Service)
old := registry.ServiceRegistry().Get(name)
if old == nil {
writeError(ctx, ErrNotFound)
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("service %s not found", name)))
return
}
old.Close()
req.Data.Name = req.Service
req.Data.Name = name
svc, err := parser.ParseService(&req.Data)
if err != nil {
writeError(ctx, ErrCreate)
writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create service %s failed: %s", name, err.Error())))
return
}
registry.ServiceRegistry().Unregister(req.Service)
registry.ServiceRegistry().Unregister(name)
if err := registry.ServiceRegistry().Register(req.Service, svc); err != nil {
if err := registry.ServiceRegistry().Register(name, svc); err != nil {
svc.Close()
writeError(ctx, ErrDup)
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("service %s already exists", name)))
return
}
@ -126,7 +132,7 @@ func updateService(ctx *gin.Context) {
config.OnUpdate(func(c *config.Config) error {
for i := range c.Services {
if c.Services[i].Name == req.Service {
if c.Services[i].Name == name {
c.Services[i] = &req.Data
break
}
@ -166,20 +172,22 @@ func deleteService(ctx *gin.Context) {
var req deleteServiceRequest
ctx.ShouldBindUri(&req)
svc := registry.ServiceRegistry().Get(req.Service)
name := strings.TrimSpace(req.Service)
svc := registry.ServiceRegistry().Get(name)
if svc == nil {
writeError(ctx, ErrNotFound)
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("service %s not found", name)))
return
}
registry.ServiceRegistry().Unregister(req.Service)
registry.ServiceRegistry().Unregister(name)
svc.Close()
config.OnUpdate(func(c *config.Config) error {
services := c.Services
c.Services = nil
for _, s := range services {
if s.Name == req.Service {
if s.Name == name {
continue
}
c.Services = append(c.Services, s)

View File

@ -15,6 +15,16 @@ var (
ErrSave = &Error{statusCode: http.StatusInternalServerError, Code: 40005, Msg: "save config failed"}
)
type ErrCode int
const (
ErrCodeInvalid = 40001
ErrCodeDup = 40002
ErrCodeFailed = 40003
ErrCodeNotFound = 40004
ErrCodeSaveConfigFailed = 40005
)
// Error is an api error.
type Error struct {
statusCode int
@ -22,6 +32,14 @@ type Error struct {
Msg string `json:"msg"`
}
func NewError(status, code int, msg string) error {
return &Error{
statusCode: status,
Code: code,
Msg: msg,
}
}
func (e *Error) Error() string {
b, _ := json.Marshal(e)
return string(b)

View File

@ -36,7 +36,12 @@ func mwBasicAuth(auther auth.Authenticator) gin.HandlerFunc {
}
u, p, _ := c.Request.BasicAuth()
if _, ok := auther.Authenticate(c, u, p); !ok {
c.AbortWithStatus(http.StatusUnauthorized)
c.Writer.Header().Set("WWW-Authenticate", "Basic")
c.JSON(http.StatusUnauthorized, Response{
Code: http.StatusUnauthorized,
Msg: "Unauthorized",
})
c.Abort()
}
}
}

View File

@ -1,156 +0,0 @@
package api
import (
"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"
)
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
}
func NewService(addr string, opts ...Option) (service.Service, error) {
ln, err := net.Listen("tcp", addr)
if err != nil {
return nil, err
}
var options options
for _, opt := range opts {
opt(&options)
}
gin.SetMode(gin.ReleaseMode)
r := gin.New()
r.Use(
cors.New((cors.Config{
AllowAllOrigins: true,
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"*"},
})),
gin.Recovery(),
)
if options.accessLog {
r.Use(mwLogger())
}
router := r.Group("")
if options.pathPrefix != "" {
router = router.Group(options.pathPrefix)
}
router.StaticFS("/docs", http.FS(swaggerDoc))
config := router.Group("/config")
config.Use(mwBasicAuth(options.auther))
registerConfig(config)
return &server{
s: &http.Server{
Handler: r,
},
ln: ln,
}, 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 registerConfig(config *gin.RouterGroup) {
config.GET("", getConfig)
config.POST("", saveConfig)
config.POST("/services", createService)
config.PUT("/services/:service", updateService)
config.DELETE("/services/:service", deleteService)
config.POST("/chains", createChain)
config.PUT("/chains/:chain", updateChain)
config.DELETE("/chains/:chain", deleteChain)
config.POST("/hops", createHop)
config.PUT("/hops/:hop", updateHop)
config.DELETE("/hops/:hop", deleteHop)
config.POST("/authers", createAuther)
config.PUT("/authers/:auther", updateAuther)
config.DELETE("/authers/:auther", deleteAuther)
config.POST("/admissions", createAdmission)
config.PUT("/admissions/:admission", updateAdmission)
config.DELETE("/admissions/:admission", deleteAdmission)
config.POST("/bypasses", createBypass)
config.PUT("/bypasses/:bypass", updateBypass)
config.DELETE("/bypasses/:bypass", deleteBypass)
config.POST("/resolvers", createResolver)
config.PUT("/resolvers/:resolver", updateResolver)
config.DELETE("/resolvers/:resolver", deleteResolver)
config.POST("/hosts", createHosts)
config.PUT("/hosts/:hosts", updateHosts)
config.DELETE("/hosts/:hosts", deleteHosts)
config.POST("/ingresses", createIngress)
config.PUT("/ingresses/:ingress", updateIngress)
config.DELETE("/ingresses/:ingress", deleteIngress)
config.POST("/routers", createRouter)
config.PUT("/routers/:router", updateRouter)
config.DELETE("/routers/:router", deleteRouter)
config.POST("/limiters", createLimiter)
config.PUT("/limiters/:limiter", updateLimiter)
config.DELETE("/limiters/:limiter", deleteLimiter)
config.POST("/climiters", createConnLimiter)
config.PUT("/climiters/:limiter", updateConnLimiter)
config.DELETE("/climiters/:limiter", deleteConnLimiter)
config.POST("/rlimiters", createRateLimiter)
config.PUT("/rlimiters/:limiter", updateRateLimiter)
config.DELETE("/rlimiters/:limiter", deleteRateLimiter)
}

View File

@ -188,8 +188,18 @@ definitions:
x-go-name: Limiters
log:
$ref: '#/definitions/LogConfig'
loggers:
items:
$ref: '#/definitions/LoggerConfig'
type: array
x-go-name: Loggers
metrics:
$ref: '#/definitions/MetricsConfig'
observers:
items:
$ref: '#/definitions/ObserverConfig'
type: array
x-go-name: Observers
profiling:
$ref: '#/definitions/ProfilingConfig'
recorders:
@ -296,11 +306,18 @@ definitions:
type: string
type: array
x-go-name: Bypasses
filter:
$ref: '#/definitions/NodeFilterConfig'
host:
description: DEPRECATED by filter.host
type: string
x-go-name: Host
http:
$ref: '#/definitions/HTTPNodeConfig'
metadata:
additionalProperties: {}
type: object
x-go-name: Metadata
name:
type: string
x-go-name: Name
@ -308,9 +325,11 @@ definitions:
type: string
x-go-name: Network
path:
description: DEPRECATED by filter.path
type: string
x-go-name: Path
protocol:
description: DEPRECATED by filter.protocol
type: string
x-go-name: Protocol
tls:
@ -319,7 +338,12 @@ definitions:
x-go-package: github.com/go-gost/x/config
ForwarderConfig:
properties:
hop:
description: the referenced hop name
type: string
x-go-name: Hop
name:
description: DEPRECATED by hop field
type: string
x-go-name: Name
nodes:
@ -342,6 +366,8 @@ definitions:
x-go-package: github.com/go-gost/x/config
HTTPNodeConfig:
properties:
auth:
$ref: '#/definitions/AuthConfig'
header:
additionalProperties:
type: string
@ -350,6 +376,11 @@ definitions:
host:
type: string
x-go-name: Host
rewrite:
items:
$ref: '#/definitions/HTTPURLRewriteConfig'
type: array
x-go-name: Rewrite
type: object
x-go-package: github.com/go-gost/x/config
HTTPRecorder:
@ -361,6 +392,14 @@ definitions:
x-go-name: URL
type: object
x-go-package: github.com/go-gost/x/config
HTTPURLRewriteConfig:
properties:
Match:
type: string
Replacement:
type: string
type: object
x-go-package: github.com/go-gost/x/config
HandlerConfig:
properties:
auth:
@ -385,6 +424,9 @@ definitions:
additionalProperties: {}
type: object
x-go-name: Metadata
observer:
type: string
x-go-name: Observer
retries:
format: int64
type: integer
@ -615,6 +657,15 @@ definitions:
x-go-name: MaxSize
type: object
x-go-package: github.com/go-gost/x/config
LoggerConfig:
properties:
log:
$ref: '#/definitions/LogConfig'
name:
type: string
x-go-name: Name
type: object
x-go-package: github.com/go-gost/x/config
MetricsConfig:
properties:
addr:
@ -664,8 +715,6 @@ definitions:
addr:
type: string
x-go-name: Addr
auth:
$ref: '#/definitions/AuthConfig'
bypass:
type: string
x-go-name: Bypass
@ -678,9 +727,8 @@ definitions:
$ref: '#/definitions/ConnectorConfig'
dialer:
$ref: '#/definitions/DialerConfig'
host:
type: string
x-go-name: Host
filter:
$ref: '#/definitions/NodeFilterConfig'
hosts:
type: string
x-go-name: Hosts
@ -699,12 +747,6 @@ definitions:
network:
type: string
x-go-name: Network
path:
type: string
x-go-name: Path
protocol:
type: string
x-go-name: Protocol
resolver:
type: string
x-go-name: Resolver
@ -714,6 +756,28 @@ definitions:
$ref: '#/definitions/TLSNodeConfig'
type: object
x-go-package: github.com/go-gost/x/config
NodeFilterConfig:
properties:
host:
type: string
x-go-name: Host
path:
type: string
x-go-name: Path
protocol:
type: string
x-go-name: Protocol
type: object
x-go-package: github.com/go-gost/x/config
ObserverConfig:
properties:
name:
type: string
x-go-name: Name
plugin:
$ref: '#/definitions/PluginConfig'
type: object
x-go-package: github.com/go-gost/x/config
PluginConfig:
properties:
addr:
@ -927,6 +991,14 @@ definitions:
x-go-name: Limiter
listener:
$ref: '#/definitions/ListenerConfig'
logger:
type: string
x-go-name: Logger
loggers:
items:
type: string
type: array
x-go-name: Loggers
metadata:
additionalProperties: {}
type: object
@ -934,6 +1006,9 @@ definitions:
name:
type: string
x-go-name: Name
observer:
type: string
x-go-name: Observer
recorders:
items:
$ref: '#/definitions/RecorderObject'
@ -947,6 +1022,61 @@ definitions:
x-go-name: RLimiter
sockopts:
$ref: '#/definitions/SockOptsConfig'
status:
$ref: '#/definitions/ServiceStatus'
type: object
x-go-package: github.com/go-gost/x/config
ServiceEvent:
properties:
msg:
type: string
x-go-name: Msg
time:
format: int64
type: integer
x-go-name: Time
type: object
x-go-package: github.com/go-gost/x/config
ServiceStats:
properties:
currentConns:
format: uint64
type: integer
x-go-name: CurrentConns
inputBytes:
format: uint64
type: integer
x-go-name: InputBytes
outputBytes:
format: uint64
type: integer
x-go-name: OutputBytes
totalConns:
format: uint64
type: integer
x-go-name: TotalConns
totalErrs:
format: uint64
type: integer
x-go-name: TotalErrs
type: object
x-go-package: github.com/go-gost/x/config
ServiceStatus:
properties:
createTime:
format: int64
type: integer
x-go-name: CreateTime
events:
items:
$ref: '#/definitions/ServiceEvent'
type: array
x-go-name: Events
state:
type: string
x-go-name: State
stats:
$ref: '#/definitions/ServiceStats'
type: object
x-go-package: github.com/go-gost/x/config
SockOptsConfig:
@ -1588,6 +1718,122 @@ paths:
summary: Update limiter by name, the limiter must already exist.
tags:
- Limiter
/config/observers:
post:
operationId: createObserverRequest
parameters:
- in: body
name: data
schema:
$ref: '#/definitions/ObserverConfig'
x-go-name: Data
responses:
"200":
$ref: '#/responses/createObserverResponse'
security:
- basicAuth:
- '[]'
summary: Create a new observer, the name of the observer must be unique in observer list.
tags:
- Observer
/config/observers/{observer}:
delete:
operationId: deleteObserverRequest
parameters:
- in: path
name: observer
required: true
type: string
x-go-name: Observer
responses:
"200":
$ref: '#/responses/deleteObserverResponse'
security:
- basicAuth:
- '[]'
summary: Delete observer by name.
tags:
- Observer
put:
operationId: updateObserverRequest
parameters:
- in: path
name: observer
required: true
type: string
x-go-name: Observer
- in: body
name: data
schema:
$ref: '#/definitions/ObserverConfig'
x-go-name: Data
responses:
"200":
$ref: '#/responses/updateObserverResponse'
security:
- basicAuth:
- '[]'
summary: Update observer by name, the observer must already exist.
tags:
- Observer
/config/recorders:
post:
operationId: createRecorderRequest
parameters:
- in: body
name: data
schema:
$ref: '#/definitions/RecorderConfig'
x-go-name: Data
responses:
"200":
$ref: '#/responses/createRecorderResponse'
security:
- basicAuth:
- '[]'
summary: Create a new recorder, the name of the recorder must be unique in recorder list.
tags:
- Recorder
/config/recorders/{recorder}:
delete:
operationId: deleteRecorderRequest
parameters:
- in: path
name: recorder
required: true
type: string
x-go-name: Recorder
responses:
"200":
$ref: '#/responses/deleteRecorderResponse'
security:
- basicAuth:
- '[]'
summary: Delete recorder by name.
tags:
- Recorder
put:
operationId: updateRecorderRequest
parameters:
- in: path
name: recorder
required: true
type: string
x-go-name: Recorder
- in: body
name: data
schema:
$ref: '#/definitions/RecorderConfig'
x-go-name: Data
responses:
"200":
$ref: '#/responses/updateRecorderResponse'
security:
- basicAuth:
- '[]'
summary: Update recorder by name, the recorder must already exist.
tags:
- Recorder
/config/resolvers:
post:
operationId: createResolverRequest
@ -1762,6 +2008,64 @@ paths:
summary: Update router by name, the router must already exist.
tags:
- Router
/config/sds:
post:
operationId: createSDRequest
parameters:
- in: body
name: data
schema:
$ref: '#/definitions/SDConfig'
x-go-name: Data
responses:
"200":
$ref: '#/responses/createSDResponse'
security:
- basicAuth:
- '[]'
summary: Create a new SD, the name of the SD must be unique in SD list.
tags:
- SD
/config/sds/{sd}:
delete:
operationId: deleteSDRequest
parameters:
- in: path
name: sd
required: true
type: string
x-go-name: SD
responses:
"200":
$ref: '#/responses/deleteSDResponse'
security:
- basicAuth:
- '[]'
summary: Delete SD by name.
tags:
- SD
put:
operationId: updateSDRequest
parameters:
- in: path
name: sd
required: true
type: string
x-go-name: SD
- in: body
name: data
schema:
$ref: '#/definitions/SDConfig'
x-go-name: Data
responses:
"200":
$ref: '#/responses/updateSDResponse'
security:
- basicAuth:
- '[]'
summary: Update SD by name, the SD must already exist.
tags:
- SD
/config/services:
post:
operationId: createServiceRequest
@ -1877,12 +2181,24 @@ responses:
Data: {}
schema:
$ref: '#/definitions/Response'
createObserverResponse:
description: successful operation.
headers:
Data: {}
schema:
$ref: '#/definitions/Response'
createRateLimiterResponse:
description: successful operation.
headers:
Data: {}
schema:
$ref: '#/definitions/Response'
createRecorderResponse:
description: successful operation.
headers:
Data: {}
schema:
$ref: '#/definitions/Response'
createResolverResponse:
description: successful operation.
headers:
@ -1895,6 +2211,12 @@ responses:
Data: {}
schema:
$ref: '#/definitions/Response'
createSDResponse:
description: successful operation.
headers:
Data: {}
schema:
$ref: '#/definitions/Response'
createServiceResponse:
description: successful operation.
headers:
@ -1955,12 +2277,24 @@ responses:
Data: {}
schema:
$ref: '#/definitions/Response'
deleteObserverResponse:
description: successful operation.
headers:
Data: {}
schema:
$ref: '#/definitions/Response'
deleteRateLimiterResponse:
description: successful operation.
headers:
Data: {}
schema:
$ref: '#/definitions/Response'
deleteRecorderResponse:
description: successful operation.
headers:
Data: {}
schema:
$ref: '#/definitions/Response'
deleteResolverResponse:
description: successful operation.
headers:
@ -1973,6 +2307,12 @@ responses:
Data: {}
schema:
$ref: '#/definitions/Response'
deleteSDResponse:
description: successful operation.
headers:
Data: {}
schema:
$ref: '#/definitions/Response'
deleteServiceResponse:
description: successful operation.
headers:
@ -2045,12 +2385,24 @@ responses:
Data: {}
schema:
$ref: '#/definitions/Response'
updateObserverResponse:
description: successful operation.
headers:
Data: {}
schema:
$ref: '#/definitions/Response'
updateRateLimiterResponse:
description: successful operation.
headers:
Data: {}
schema:
$ref: '#/definitions/Response'
updateRecorderResponse:
description: successful operation.
headers:
Data: {}
schema:
$ref: '#/definitions/Response'
updateResolverResponse:
description: successful operation.
headers:
@ -2063,6 +2415,12 @@ responses:
Data: {}
schema:
$ref: '#/definitions/Response'
updateSDResponse:
description: successful operation.
headers:
Data: {}
schema:
$ref: '#/definitions/Response'
updateServiceResponse:
description: successful operation.
headers:

View File

@ -110,7 +110,7 @@ func (p *authenticator) Authenticate(ctx context.Context, user, password string,
}
v, ok := p.kvs[user]
return "", ok && (v == "" || password == v)
return user, ok && (v == "" || password == v)
}
func (p *authenticator) periodReload(ctx context.Context) error {
@ -145,6 +145,8 @@ func (p *authenticator) reload(ctx context.Context) (err error) {
kvs[k] = v
}
p.options.logger.Debugf("load items %d", len(m))
p.mu.Lock()
defer p.mu.Unlock()
@ -206,7 +208,6 @@ func (p *authenticator) load(ctx context.Context) (m map[string]string, err erro
}
}
p.options.logger.Debugf("load items %d", len(m))
return
}

72
auth/plugin/grpc.go Normal file
View File

@ -0,0 +1,72 @@
package auth
import (
"context"
"io"
"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"
"github.com/go-gost/x/internal/plugin"
"google.golang.org/grpc"
)
type grpcPlugin struct {
conn grpc.ClientConnInterface
client proto.AuthenticatorClient
log logger.Logger
}
// NewGRPCPlugin creates an Authenticator plugin based on gRPC.
func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) auth.Authenticator {
var options plugin.Options
for _, opt := range opts {
opt(&options)
}
log := logger.Default().WithFields(map[string]any{
"kind": "auther",
"auther": name,
})
conn, err := plugin.NewGRPCConn(addr, &options)
if err != nil {
log.Error(err)
}
p := &grpcPlugin{
conn: conn,
log: log,
}
if conn != nil {
p.client = proto.NewAuthenticatorClient(conn)
}
return p
}
// Authenticate checks the validity of the provided user-password pair.
func (p *grpcPlugin) Authenticate(ctx context.Context, user, password string, opts ...auth.Option) (string, bool) {
if p.client == nil {
return "", false
}
r, err := p.client.Authenticate(ctx,
&proto.AuthenticateRequest{
Username: user,
Password: password,
Client: string(ctxvalue.ClientAddrFromContext(ctx)),
})
if err != nil {
p.log.Error(err)
return "", false
}
return r.Id, r.Ok
}
func (p *grpcPlugin) Close() error {
if closer, ok := p.conn.(io.Closer); ok {
return closer.Close()
}
return nil
}

View File

@ -4,76 +4,14 @@ import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"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/internal/ctx"
ctxvalue "github.com/go-gost/x/ctx"
"github.com/go-gost/x/internal/plugin"
"google.golang.org/grpc"
)
type grpcPlugin struct {
conn grpc.ClientConnInterface
client proto.AuthenticatorClient
log logger.Logger
}
// NewGRPCPlugin creates an Authenticator plugin based on gRPC.
func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) auth.Authenticator {
var options plugin.Options
for _, opt := range opts {
opt(&options)
}
log := logger.Default().WithFields(map[string]any{
"kind": "auther",
"auther": name,
})
conn, err := plugin.NewGRPCConn(addr, &options)
if err != nil {
log.Error(err)
}
p := &grpcPlugin{
conn: conn,
log: log,
}
if conn != nil {
p.client = proto.NewAuthenticatorClient(conn)
}
return p
}
// Authenticate checks the validity of the provided user-password pair.
func (p *grpcPlugin) Authenticate(ctx context.Context, user, password string, opts ...auth.Option) (string, bool) {
if p.client == nil {
return "", false
}
r, err := p.client.Authenticate(ctx,
&proto.AuthenticateRequest{
Username: user,
Password: password,
Client: string(ctxvalue.ClientAddrFromContext(ctx)),
})
if err != nil {
p.log.Error(err)
return "", false
}
return r.Id, r.Ok
}
func (p *grpcPlugin) Close() error {
if closer, ok := p.conn.(io.Closer); ok {
return closer.Close()
}
return nil
}
type httpPluginRequest struct {
Username string `json:"username"`
Password string `json:"password"`

View File

@ -130,6 +130,7 @@ func (bp *localBypass) reload(ctx context.Context) error {
return err
}
patterns := append(bp.options.matchers, v...)
bp.options.logger.Debugf("load items %d", len(patterns))
var addrs []string
var inets []*net.IPNet
@ -205,7 +206,6 @@ func (bp *localBypass) load(ctx context.Context) (patterns []string, err error)
}
}
bp.options.logger.Debugf("load items %d", len(patterns))
return
}
@ -235,11 +235,17 @@ func (bp *localBypass) Contains(ctx context.Context, network, addr string, opts
b := !bp.options.whitelist && matched ||
bp.options.whitelist && !matched
if b {
bp.options.logger.Debugf("bypass: %s", addr)
bp.options.logger.Debugf("bypass: %s, whitelist: %t", addr, bp.options.whitelist)
} else {
bp.options.logger.Debugf("pass: %s, whitelist: %t", addr, bp.options.whitelist)
}
return b
}
func (p *localBypass) IsWhitelist() bool {
return p.options.whitelist
}
func (bp *localBypass) parseLine(s string) string {
if n := strings.IndexByte(s, '#'); n >= 0 {
s = s[:n]

81
bypass/plugin/grpc.go Normal file
View File

@ -0,0 +1,81 @@
package bypass
import (
"context"
"io"
"github.com/go-gost/core/bypass"
"github.com/go-gost/core/logger"
"github.com/go-gost/plugin/bypass/proto"
ctxvalue "github.com/go-gost/x/ctx"
"github.com/go-gost/x/internal/plugin"
"google.golang.org/grpc"
)
type grpcPlugin struct {
conn grpc.ClientConnInterface
client proto.BypassClient
log logger.Logger
}
// NewGRPCPlugin creates a Bypass plugin based on gRPC.
func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) bypass.Bypass {
var options plugin.Options
for _, opt := range opts {
opt(&options)
}
log := logger.Default().WithFields(map[string]any{
"kind": "bypass",
"bypass": name,
})
conn, err := plugin.NewGRPCConn(addr, &options)
if err != nil {
log.Error(err)
}
p := &grpcPlugin{
conn: conn,
log: log,
}
if conn != nil {
p.client = proto.NewBypassClient(conn)
}
return p
}
func (p *grpcPlugin) Contains(ctx context.Context, network, addr string, opts ...bypass.Option) bool {
if p.client == nil {
return true
}
var options bypass.Options
for _, opt := range opts {
opt(&options)
}
r, err := p.client.Bypass(ctx,
&proto.BypassRequest{
Network: network,
Addr: addr,
Client: string(ctxvalue.ClientIDFromContext(ctx)),
Host: options.Host,
Path: options.Path,
})
if err != nil {
p.log.Error(err)
return true
}
return r.Ok
}
func (p *grpcPlugin) Close() error {
if closer, ok := p.conn.(io.Closer); ok {
return closer.Close()
}
return nil
}
func (p *grpcPlugin) IsWhitelist() bool {
return false
}

View File

@ -4,81 +4,14 @@ import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"github.com/go-gost/core/bypass"
"github.com/go-gost/core/logger"
"github.com/go-gost/plugin/bypass/proto"
ctxvalue "github.com/go-gost/x/internal/ctx"
ctxvalue "github.com/go-gost/x/ctx"
"github.com/go-gost/x/internal/plugin"
"google.golang.org/grpc"
)
type grpcPlugin struct {
conn grpc.ClientConnInterface
client proto.BypassClient
log logger.Logger
}
// NewGRPCPlugin creates a Bypass plugin based on gRPC.
func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) bypass.Bypass {
var options plugin.Options
for _, opt := range opts {
opt(&options)
}
log := logger.Default().WithFields(map[string]any{
"kind": "bypass",
"bypass": name,
})
conn, err := plugin.NewGRPCConn(addr, &options)
if err != nil {
log.Error(err)
}
p := &grpcPlugin{
conn: conn,
log: log,
}
if conn != nil {
p.client = proto.NewBypassClient(conn)
}
return p
}
func (p *grpcPlugin) Contains(ctx context.Context, network, addr string, opts ...bypass.Option) bool {
if p.client == nil {
return true
}
var options bypass.Options
for _, opt := range opts {
opt(&options)
}
r, err := p.client.Bypass(ctx,
&proto.BypassRequest{
Network: network,
Addr: addr,
Client: string(ctxvalue.ClientIDFromContext(ctx)),
Host: options.Host,
Path: options.Path,
})
if err != nil {
p.log.Error(err)
return true
}
return r.Ok
}
func (p *grpcPlugin) Close() error {
if closer, ok := p.conn.(io.Closer); ok {
return closer.Close()
}
return nil
}
type httpPluginRequest struct {
Network string `json:"network"`
Addr string `json:"addr"`
@ -163,3 +96,7 @@ func (p *httpPlugin) Contains(ctx context.Context, network, addr string, opts ..
}
return res.OK
}
func (p *httpPlugin) IsWhitelist() bool {
return false
}

View File

@ -104,7 +104,7 @@ func (c *Chain) Route(ctx context.Context, network, address string, opts ...chai
tr.Options().Route = rt
node = node.Copy()
node.Options().Transport = tr
rt = NewRoute()
rt = NewRoute(ChainRouteOption(c))
}
rt.addNode(node)

View File

@ -129,10 +129,11 @@ func (r *route) connect(ctx context.Context, logger logger.Logger) (conn net.Con
metrics.Labels{"chain": name, "node": node.Name}); v != nil {
v.Inc()
}
} else {
if marker != nil {
marker.Reset()
}
return
}
if marker != nil {
marker.Reset()
}
}
}()

View File

@ -297,9 +297,9 @@ type RedisRecorder struct {
}
type RecorderObject struct {
Name string `json:"name"`
Record string `json:"record"`
Metadata map[string]any
Name string `json:"name"`
Record string `json:"record"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
}
type LimiterConfig struct {
@ -312,6 +312,11 @@ type LimiterConfig struct {
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
}
type ObserverConfig struct {
Name string `json:"name"`
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
}
type ListenerConfig struct {
Type string `json:"type"`
Chain string `yaml:",omitempty" json:"chain,omitempty"`
@ -333,32 +338,55 @@ type HandlerConfig struct {
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
Limiter string `yaml:",omitempty" json:"limiter,omitempty"`
Observer string `yaml:",omitempty" json:"observer,omitempty"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
}
type ForwarderConfig struct {
Name string `yaml:",omitempty" json:"name,omitempty"`
// DEPRECATED by hop field
Name string `yaml:",omitempty" json:"name,omitempty"`
// the referenced hop name
Hop string `yaml:",omitempty" json:"hop,omitempty"`
Selector *SelectorConfig `yaml:",omitempty" json:"selector,omitempty"`
Nodes []*ForwardNodeConfig `json:"nodes"`
}
type ForwardNodeConfig struct {
Name string `yaml:",omitempty" json:"name,omitempty"`
Addr string `yaml:",omitempty" json:"addr,omitempty"`
Host string `yaml:",omitempty" json:"host,omitempty"`
Network string `yaml:",omitempty" json:"network,omitempty"`
Protocol string `yaml:",omitempty" json:"protocol,omitempty"`
Path string `yaml:",omitempty" json:"path,omitempty"`
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"`
TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"`
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
Name string `yaml:",omitempty" json:"name,omitempty"`
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"`
// DEPRECATED by filter.protocol
Protocol string `yaml:",omitempty" json:"protocol,omitempty"`
// DEPRECATED by filter.host
Host string `yaml:",omitempty" json:"host,omitempty"`
// DEPRECATED by filter.path
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"`
}
type HTTPURLRewriteConfig struct {
Match string
Replacement string
}
type NodeFilterConfig struct {
Host string `yaml:",omitempty" json:"host,omitempty"`
Protocol string `yaml:",omitempty" json:"protocol,omitempty"`
Path string `yaml:",omitempty" json:"path,omitempty"`
}
type HTTPNodeConfig struct {
Host string `yaml:",omitempty" json:"host,omitempty"`
Header map[string]string `yaml:",omitempty" json:"header,omitempty"`
Host string `yaml:",omitempty" json:"host,omitempty"`
Header map[string]string `yaml:",omitempty" json:"header,omitempty"`
Rewrite []HTTPURLRewriteConfig `yaml:",omitempty" json:"rewrite,omitempty"`
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
}
type TLSNodeConfig struct {
@ -402,11 +430,35 @@ type ServiceConfig struct {
CLimiter string `yaml:"climiter,omitempty" json:"climiter,omitempty"`
RLimiter string `yaml:"rlimiter,omitempty" json:"rlimiter,omitempty"`
Logger string `yaml:",omitempty" json:"logger,omitempty"`
Loggers []string `yaml:",omitempty" json:"loggers,omitempty"`
Observer string `yaml:",omitempty" json:"observer,omitempty"`
Recorders []*RecorderObject `yaml:",omitempty" json:"recorders,omitempty"`
Handler *HandlerConfig `yaml:",omitempty" json:"handler,omitempty"`
Listener *ListenerConfig `yaml:",omitempty" json:"listener,omitempty"`
Forwarder *ForwarderConfig `yaml:",omitempty" json:"forwarder,omitempty"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
// service status, read-only
Status *ServiceStatus `yaml:",omitempty" json:"status,omitempty"`
}
type ServiceStatus struct {
CreateTime int64 `yaml:"createTime" json:"createTime"`
State string `yaml:"state" json:"state"`
Events []ServiceEvent `yaml:",omitempty" json:"events,omitempty"`
Stats *ServiceStats `yaml:",omitempty" json:"stats,omitempty"`
}
type ServiceEvent struct {
Time int64 `yaml:"time" json:"time"`
Msg string `yaml:"msg" json:"msg"`
}
type ServiceStats struct {
TotalConns uint64 `yaml:"totalConns" json:"totalConns"`
CurrentConns uint64 `yaml:"currentConns" json:"currentConns"`
TotalErrs uint64 `yaml:"totalErrs" json:"totalErrs"`
InputBytes uint64 `yaml:"inputBytes" json:"inputBytes"`
OutputBytes uint64 `yaml:"outputBytes" json:"outputBytes"`
}
type ChainConfig struct {
@ -435,27 +487,26 @@ type HopConfig struct {
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"`
Host string `yaml:",omitempty" json:"host,omitempty"`
Network string `yaml:",omitempty" json:"network,omitempty"`
Protocol string `yaml:",omitempty" json:"protocol,omitempty"`
Path string `yaml:",omitempty" json:"path,omitempty"`
Interface string `yaml:",omitempty" json:"interface,omitempty"`
SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,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"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"`
TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"`
Auth *AuthConfig `yaml:",omitempty" json:"auth,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"`
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"`
}
type Config struct {
@ -474,6 +525,7 @@ type Config struct {
Limiters []*LimiterConfig `yaml:",omitempty" json:"limiters,omitempty"`
CLimiters []*LimiterConfig `yaml:"climiters,omitempty" json:"climiters,omitempty"`
RLimiters []*LimiterConfig `yaml:"rlimiters,omitempty" json:"rlimiters,omitempty"`
Observers []*ObserverConfig `yaml:",omitempty" json:"observers,omitempty"`
Loggers []*LoggerConfig `yaml:",omitempty" json:"loggers,omitempty"`
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
Log *LogConfig `yaml:",omitempty" json:"log,omitempty"`

View File

@ -7,6 +7,7 @@ import (
"github.com/go-gost/core/admission"
"github.com/go-gost/core/logger"
xadmission "github.com/go-gost/x/admission"
admission_plugin "github.com/go-gost/x/admission/plugin"
"github.com/go-gost/x/config"
"github.com/go-gost/x/internal/loader"
"github.com/go-gost/x/internal/plugin"
@ -28,13 +29,13 @@ func ParseAdmission(cfg *config.AdmissionConfig) admission.Admission {
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return xadmission.NewHTTPPlugin(
return admission_plugin.NewHTTPPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
)
default:
return xadmission.NewGRPCPlugin(
return admission_plugin.NewGRPCPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),

View File

@ -7,6 +7,7 @@ import (
"github.com/go-gost/core/auth"
"github.com/go-gost/core/logger"
xauth "github.com/go-gost/x/auth"
auth_plugin "github.com/go-gost/x/auth/plugin"
"github.com/go-gost/x/config"
"github.com/go-gost/x/internal/loader"
"github.com/go-gost/x/internal/plugin"
@ -28,13 +29,13 @@ func ParseAuther(cfg *config.AutherConfig) auth.Authenticator {
}
switch cfg.Plugin.Type {
case "http":
return xauth.NewHTTPPlugin(
return auth_plugin.NewHTTPPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
)
default:
return xauth.NewGRPCPlugin(
return auth_plugin.NewGRPCPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),

View File

@ -7,6 +7,7 @@ import (
"github.com/go-gost/core/bypass"
"github.com/go-gost/core/logger"
xbypass "github.com/go-gost/x/bypass"
bypass_plugin "github.com/go-gost/x/bypass/plugin"
"github.com/go-gost/x/config"
"github.com/go-gost/x/internal/loader"
"github.com/go-gost/x/internal/plugin"
@ -28,13 +29,13 @@ func ParseBypass(cfg *config.BypassConfig) bypass.Bypass {
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return xbypass.NewHTTPPlugin(
return bypass_plugin.NewHTTPPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
)
default:
return xbypass.NewGRPCPlugin(
return bypass_plugin.NewGRPCPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),

View File

@ -12,12 +12,12 @@ import (
"github.com/go-gost/x/registry"
)
func ParseChain(cfg *config.ChainConfig) (chain.Chainer, error) {
func ParseChain(cfg *config.ChainConfig, log logger.Logger) (chain.Chainer, error) {
if cfg == nil {
return nil, nil
}
chainLogger := logger.Default().WithFields(map[string]any{
chainLogger := log.WithFields(map[string]any{
"kind": "chain",
"chain": cfg.Name,
})
@ -37,7 +37,7 @@ func ParseChain(cfg *config.ChainConfig) (chain.Chainer, error) {
var err error
if ch.Nodes != nil || ch.Plugin != nil {
if hop, err = hop_parser.ParseHop(ch); err != nil {
if hop, err = hop_parser.ParseHop(ch, log); err != nil {
return nil, err
}
} else {

View File

@ -8,16 +8,20 @@ import (
"github.com/go-gost/core/chain"
"github.com/go-gost/core/hop"
"github.com/go-gost/core/logger"
mdutil "github.com/go-gost/core/metadata/util"
"github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing"
bypass_parser "github.com/go-gost/x/config/parsing/bypass"
node_parser "github.com/go-gost/x/config/parsing/node"
selector_parser "github.com/go-gost/x/config/parsing/selector"
xhop "github.com/go-gost/x/hop"
hop_plugin "github.com/go-gost/x/hop/plugin"
"github.com/go-gost/x/internal/loader"
"github.com/go-gost/x/internal/plugin"
"github.com/go-gost/x/metadata"
)
func ParseHop(cfg *config.HopConfig) (hop.Hop, error) {
func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) {
if cfg == nil {
return nil, nil
}
@ -31,14 +35,14 @@ func ParseHop(cfg *config.HopConfig) (hop.Hop, error) {
}
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return xhop.NewHTTPPlugin(
case plugin.HTTP:
return hop_plugin.NewHTTPPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
), nil
default:
return xhop.NewGRPCPlugin(
return hop_plugin.NewGRPCPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),
@ -46,6 +50,16 @@ func ParseHop(cfg *config.HopConfig) (hop.Hop, error) {
}
}
ifce := cfg.Interface
var netns string
if cfg.Metadata != nil {
md := metadata.NewMetadata(cfg.Metadata)
if v := mdutil.GetString(md, parsing.MDKeyInterface); v != "" {
ifce = v
}
netns = mdutil.GetString(md, "netns")
}
var nodes []*chain.Node
for _, v := range cfg.Nodes {
if v == nil {
@ -59,25 +73,31 @@ func ParseHop(cfg *config.HopConfig) (hop.Hop, error) {
v.Hosts = cfg.Hosts
}
if v.Interface == "" {
v.Interface = cfg.Interface
v.Interface = ifce
}
if v.Netns == "" {
v.Netns = netns
}
if v.SockOpts == nil {
v.SockOpts = cfg.SockOpts
}
if v.Connector == nil {
v.Connector = &config.ConnectorConfig{
Type: "http",
}
v.Connector = &config.ConnectorConfig{}
}
if strings.TrimSpace(v.Connector.Type) == "" {
v.Connector.Type = "http"
}
if v.Dialer == nil {
v.Dialer = &config.DialerConfig{
Type: "tcp",
}
v.Dialer = &config.DialerConfig{}
}
if strings.TrimSpace(v.Dialer.Type) == "" {
v.Dialer.Type = "tcp"
}
node, err := node_parser.ParseNode(cfg.Name, v)
node, err := node_parser.ParseNode(cfg.Name, v, log)
if err != nil {
return nil, err
}
@ -97,7 +117,7 @@ func ParseHop(cfg *config.HopConfig) (hop.Hop, error) {
xhop.SelectorOption(sel),
xhop.BypassOption(bypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
xhop.ReloadPeriodOption(cfg.Reload),
xhop.LoggerOption(logger.Default().WithFields(map[string]any{
xhop.LoggerOption(log.WithFields(map[string]any{
"kind": "hop",
"hop": cfg.Name,
})),

View File

@ -9,6 +9,7 @@ import (
"github.com/go-gost/core/logger"
"github.com/go-gost/x/config"
xhosts "github.com/go-gost/x/hosts"
hosts_plugin "github.com/go-gost/x/hosts/plugin"
"github.com/go-gost/x/internal/loader"
"github.com/go-gost/x/internal/plugin"
)
@ -28,13 +29,13 @@ func ParseHostMapper(cfg *config.HostsConfig) hosts.HostMapper {
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return xhosts.NewHTTPPlugin(
return hosts_plugin.NewHTTPPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
)
default:
return xhosts.NewGRPCPlugin(
return hosts_plugin.NewGRPCPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),

View File

@ -8,6 +8,7 @@ import (
"github.com/go-gost/core/logger"
"github.com/go-gost/x/config"
xingress "github.com/go-gost/x/ingress"
ingress_plugin "github.com/go-gost/x/ingress/plugin"
"github.com/go-gost/x/internal/loader"
"github.com/go-gost/x/internal/plugin"
)
@ -27,13 +28,13 @@ func ParseIngress(cfg *config.IngressConfig) ingress.Ingress {
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return xingress.NewHTTPPlugin(
return ingress_plugin.NewHTTPPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
)
default:
return xingress.NewGRPCPlugin(
return ingress_plugin.NewGRPCPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),

View File

@ -14,6 +14,7 @@ import (
xconn "github.com/go-gost/x/limiter/conn"
xrate "github.com/go-gost/x/limiter/rate"
xtraffic "github.com/go-gost/x/limiter/traffic"
traffic_plugin "github.com/go-gost/x/limiter/traffic/plugin"
)
func ParseTrafficLimiter(cfg *config.LimiterConfig) (lim traffic.TrafficLimiter) {
@ -31,13 +32,13 @@ func ParseTrafficLimiter(cfg *config.LimiterConfig) (lim traffic.TrafficLimiter)
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return xtraffic.NewHTTPPlugin(
return traffic_plugin.NewHTTPPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
)
default:
return xtraffic.NewGRPCPlugin(
return traffic_plugin.NewGRPCPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),

View File

@ -8,6 +8,7 @@ import (
"github.com/go-gost/core/logger"
"github.com/go-gost/x/config"
xlogger "github.com/go-gost/x/logger"
"github.com/go-gost/x/registry"
"gopkg.in/natefinch/lumberjack.v2"
)
@ -53,3 +54,17 @@ func ParseLogger(cfg *config.LoggerConfig) logger.Logger {
return xlogger.NewLogger(opts...)
}
func List(name string, names ...string) []logger.Logger {
var loggers []logger.Logger
if adm := registry.LoggerRegistry().Get(name); adm != nil {
loggers = append(loggers, adm)
}
for _, s := range names {
if lg := registry.LoggerRegistry().Get(s); lg != nil {
loggers = append(loggers, lg)
}
}
return loggers
}

View File

@ -3,8 +3,8 @@ package node
import (
"fmt"
"net"
"regexp"
"strings"
"time"
"github.com/go-gost/core/bypass"
"github.com/go-gost/core/chain"
@ -23,7 +23,7 @@ import (
"github.com/go-gost/x/registry"
)
func ParseNode(hop string, cfg *config.NodeConfig) (*chain.Node, error) {
func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.Node, error) {
if cfg == nil {
return nil, nil
}
@ -40,7 +40,7 @@ func ParseNode(hop string, cfg *config.NodeConfig) (*chain.Node, error) {
}
}
nodeLogger := logger.Default().WithFields(map[string]any{
nodeLogger := log.WithFields(map[string]any{
"hop": hop,
"kind": "node",
"node": cfg.Name,
@ -142,37 +142,65 @@ func ParseNode(hop string, cfg *config.NodeConfig) (*chain.Node, error) {
tr := chain.NewTransport(d, cr,
chain.AddrTransportOption(cfg.Addr),
chain.InterfaceTransportOption(cfg.Interface),
chain.NetnsTransportOption(cfg.Netns),
chain.SockOptsTransportOption(sockOpts),
chain.TimeoutTransportOption(10*time.Second),
)
// convert *.example.com to .example.com
// convert *example.com to example.com
host := cfg.Host
if strings.HasPrefix(host, "*") {
host = host[1:]
if !strings.HasPrefix(host, ".") {
host = "." + host
}
}
opts := []chain.NodeOption{
chain.TransportNodeOption(tr),
chain.BypassNodeOption(bypass.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.HostNodeOption(host),
chain.ProtocolNodeOption(cfg.Protocol),
chain.PathNodeOption(cfg.Path),
chain.NetworkNodeOption(cfg.Network),
}
if filter := cfg.Filter; filter != nil {
// convert *.example.com to .example.com
// convert *example.com to example.com
host := filter.Host
if strings.HasPrefix(host, "*") {
host = host[1:]
if !strings.HasPrefix(host, ".") {
host = "." + host
}
}
settings := &chain.NodeFilterSettings{
Protocol: filter.Protocol,
Host: host,
Path: filter.Path,
}
opts = append(opts, chain.NodeFilterOption(settings))
}
if cfg.HTTP != nil {
opts = append(opts, chain.HTTPNodeOption(&chain.HTTPNodeSettings{
settings := &chain.HTTPNodeSettings{
Host: cfg.HTTP.Host,
Header: cfg.HTTP.Header,
}))
}
if auth := cfg.HTTP.Auth; auth != nil && auth.Username != "" {
settings.Auther = xauth.NewAuthenticator(
xauth.AuthsOption(map[string]string{auth.Username: auth.Password}),
xauth.LoggerOption(log.WithFields(map[string]any{
"kind": "node",
"node": cfg.Name,
"addr": cfg.Addr,
})),
)
}
for _, v := range cfg.HTTP.Rewrite {
if pattern, _ := regexp.Compile(v.Match); pattern != nil {
settings.Rewrite = append(settings.Rewrite, chain.HTTPURLRewriteSetting{
Pattern: pattern,
Replacement: v.Replacement,
})
}
}
opts = append(opts, chain.HTTPNodeOption(settings))
}
if cfg.TLS != nil {
tlsCfg := &chain.TLSNodeSettings{
ServerName: cfg.TLS.ServerName,
@ -185,18 +213,5 @@ func ParseNode(hop string, cfg *config.NodeConfig) (*chain.Node, error) {
}
opts = append(opts, chain.TLSNodeOption(tlsCfg))
}
if cfg.Auth != nil {
opts = append(opts, chain.AutherNodeOption(
xauth.NewAuthenticator(
xauth.AuthsOption(map[string]string{cfg.Auth.Username: cfg.Auth.Password}),
xauth.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "node",
"node": cfg.Name,
"addr": cfg.Addr,
"host": cfg.Host,
"protocol": cfg.Protocol,
})),
)))
}
return chain.NewNode(cfg.Name, cfg.Addr, opts...), nil
}

View File

@ -0,0 +1,39 @@
package observer
import (
"crypto/tls"
"strings"
"github.com/go-gost/core/observer"
"github.com/go-gost/x/config"
"github.com/go-gost/x/internal/plugin"
observer_plugin "github.com/go-gost/x/observer/plugin"
)
func ParseObserver(cfg *config.ObserverConfig) observer.Observer {
if cfg == nil || cfg.Plugin == nil {
return nil
}
var tlsCfg *tls.Config
if cfg.Plugin.TLS != nil {
tlsCfg = &tls.Config{
ServerName: cfg.Plugin.TLS.ServerName,
InsecureSkipVerify: !cfg.Plugin.TLS.Secure,
}
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return observer_plugin.NewHTTPPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
)
default:
return observer_plugin.NewGRPCPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),
)
}
}

View File

@ -10,6 +10,7 @@ const (
MDKeyPostUp = "postUp"
MDKeyPostDown = "postDown"
MDKeyIgnoreChain = "ignoreChain"
MDKeyEnableStats = "enableStats"
MDKeyRecorderDirection = "direction"
MDKeyRecorderTimestampFormat = "timeStampFormat"

View File

@ -8,6 +8,7 @@ import (
"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"
)
func ParseRecorder(cfg *config.RecorderConfig) (r recorder.Recorder) {
@ -25,13 +26,13 @@ func ParseRecorder(cfg *config.RecorderConfig) (r recorder.Recorder) {
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return xrecorder.NewHTTPPlugin(
return recorder_plugin.NewHTTPPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
)
default:
return xrecorder.NewGRPCPlugin(
return recorder_plugin.NewGRPCPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),

View File

@ -11,6 +11,7 @@ import (
"github.com/go-gost/x/internal/plugin"
"github.com/go-gost/x/registry"
xresolver "github.com/go-gost/x/resolver"
resolver_plugin "github.com/go-gost/x/resolver/plugin"
)
func ParseResolver(cfg *config.ResolverConfig) (resolver.Resolver, error) {
@ -28,13 +29,13 @@ func ParseResolver(cfg *config.ResolverConfig) (resolver.Resolver, error) {
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return xresolver.NewHTTPPlugin(
return resolver_plugin.NewHTTPPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
), nil
default:
return xresolver.NewGRPCPlugin(
return resolver_plugin.NewGRPCPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),

View File

@ -11,6 +11,7 @@ import (
"github.com/go-gost/x/internal/loader"
"github.com/go-gost/x/internal/plugin"
xrouter "github.com/go-gost/x/router"
router_plugin "github.com/go-gost/x/router/plugin"
)
func ParseRouter(cfg *config.RouterConfig) router.Router {
@ -28,13 +29,13 @@ func ParseRouter(cfg *config.RouterConfig) router.Router {
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return xrouter.NewHTTPPlugin(
return router_plugin.NewHTTPPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
)
default:
return xrouter.NewGRPCPlugin(
return router_plugin.NewGRPCPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),

View File

@ -7,7 +7,7 @@ import (
"github.com/go-gost/core/sd"
"github.com/go-gost/x/config"
"github.com/go-gost/x/internal/plugin"
xsd "github.com/go-gost/x/sd"
sd_plugin "github.com/go-gost/x/sd/plugin"
)
func ParseSD(cfg *config.SDConfig) sd.SD {
@ -24,13 +24,13 @@ func ParseSD(cfg *config.SDConfig) sd.SD {
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return xsd.NewHTTPPlugin(
return sd_plugin.NewHTTPPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
)
default:
return xsd.NewGRPCPlugin(
return sd_plugin.NewGRPCPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),

View File

@ -2,6 +2,9 @@ package service
import (
"fmt"
"runtime"
"strings"
"time"
"github.com/go-gost/core/admission"
"github.com/go-gost/core/auth"
@ -12,6 +15,7 @@ import (
"github.com/go-gost/core/listener"
"github.com/go-gost/core/logger"
mdutil "github.com/go-gost/core/metadata/util"
"github.com/go-gost/core/observer/stats"
"github.com/go-gost/core/recorder"
"github.com/go-gost/core/selector"
"github.com/go-gost/core/service"
@ -22,30 +26,36 @@ import (
auth_parser "github.com/go-gost/x/config/parsing/auth"
bypass_parser "github.com/go-gost/x/config/parsing/bypass"
hop_parser "github.com/go-gost/x/config/parsing/hop"
logger_parser "github.com/go-gost/x/config/parsing/logger"
selector_parser "github.com/go-gost/x/config/parsing/selector"
xnet "github.com/go-gost/x/internal/net"
tls_util "github.com/go-gost/x/internal/util/tls"
"github.com/go-gost/x/metadata"
"github.com/go-gost/x/registry"
xservice "github.com/go-gost/x/service"
"github.com/vishvananda/netns"
)
func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
if cfg.Listener == nil {
cfg.Listener = &config.ListenerConfig{
Type: "tcp",
}
cfg.Listener = &config.ListenerConfig{}
}
if cfg.Handler == nil {
cfg.Handler = &config.HandlerConfig{
Type: "auto",
}
if strings.TrimSpace(cfg.Listener.Type) == "" {
cfg.Listener.Type = "tcp"
}
log := registry.LoggerRegistry().Get(cfg.Logger)
if log == nil {
log = logger.Default()
if cfg.Handler == nil {
cfg.Handler = &config.HandlerConfig{}
}
if strings.TrimSpace(cfg.Handler.Type) == "" {
cfg.Handler.Type = "auto"
}
log := logger.Default()
if loggers := logger_parser.List(cfg.Logger, cfg.Loggers...); len(loggers) > 0 {
log = logger.LoggerGroup(loggers...)
}
serviceLogger := log.WithFields(map[string]any{
"kind": "service",
"service": cfg.Name,
@ -53,17 +63,13 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
"handler": cfg.Handler.Type,
})
listenerLogger := serviceLogger.WithFields(map[string]any{
"kind": "listener",
})
tlsCfg := cfg.Listener.TLS
if tlsCfg == nil {
tlsCfg = &config.TLSConfig{}
}
tlsConfig, err := tls_util.LoadServerConfig(tlsCfg)
if err != nil {
listenerLogger.Error(err)
serviceLogger.Error(err)
return nil, err
}
if tlsConfig == nil {
@ -94,6 +100,10 @@ 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 netnsIn, netnsOut string
var dialTimeout time.Duration
if cfg.Metadata != nil {
md := metadata.NewMetadata(cfg.Metadata)
ppv = mdutil.GetInt(md, parsing.MDKeyProxyProtocol)
@ -110,31 +120,77 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
postUp = mdutil.GetStrings(md, parsing.MDKeyPostUp)
postDown = mdutil.GetStrings(md, parsing.MDKeyPostDown)
ignoreChain = mdutil.GetBool(md, parsing.MDKeyIgnoreChain)
if mdutil.GetBool(md, parsing.MDKeyEnableStats) {
pStats = &stats.Stats{}
}
observePeriod = mdutil.GetDuration(md, "observePeriod")
netnsIn = mdutil.GetString(md, "netns")
netnsOut = mdutil.GetString(md, "netns.out")
dialTimeout = mdutil.GetDuration(md, "dialTimeout")
}
listenerLogger := serviceLogger.WithFields(map[string]any{
"kind": "listener",
})
routerOpts := []chain.RouterOption{
chain.TimeoutRouterOption(dialTimeout),
chain.InterfaceRouterOption(ifce),
chain.NetnsRouterOption(netnsOut),
chain.SockOptsRouterOption(sockOpts),
chain.ResolverRouterOption(registry.ResolverRegistry().Get(cfg.Resolver)),
chain.HostMapperRouterOption(registry.HostsRegistry().Get(cfg.Hosts)),
chain.LoggerRouterOption(listenerLogger),
}
if !ignoreChain {
routerOpts = append(routerOpts,
chain.ChainRouterOption(chainGroup(cfg.Listener.Chain, cfg.Listener.ChainGroup)),
)
}
listenOpts := []listener.Option{
listener.AddrOption(cfg.Addr),
listener.RouterOption(chain.NewRouter(routerOpts...)),
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.ConnLimiterOption(registry.ConnLimiterRegistry().Get(cfg.CLimiter)),
listener.LoggerOption(listenerLogger),
listener.ServiceOption(cfg.Name),
listener.ProxyProtocolOption(ppv),
listener.StatsOption(pStats),
listener.NetnsOption(netnsIn),
listener.LoggerOption(listenerLogger),
}
if !ignoreChain {
listenOpts = append(listenOpts,
listener.ChainOption(chainGroup(cfg.Listener.Chain, cfg.Listener.ChainGroup)),
)
if netnsIn != "" {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
originNs, err := netns.Get()
if err != nil {
return nil, fmt.Errorf("netns.Get(): %v", err)
}
defer netns.Set(originNs)
ns, err := netns.GetFromName(netnsIn)
if err != nil {
return nil, fmt.Errorf("netns.GetFromName(%s): %v", netnsIn, err)
}
defer ns.Close()
if err := netns.Set(ns); err != nil {
return nil, fmt.Errorf("netns.Set(%s): %v", netnsIn, err)
}
}
var ln listener.Listener
if rf := registry.ListenerRegistry().Get(cfg.Listener.Type); rf != nil {
ln = rf(listenOpts...)
} else {
return nil, fmt.Errorf("unregistered listener: %s", cfg.Listener.Type)
return nil, fmt.Errorf("unknown listener: %s", cfg.Listener.Type)
}
if cfg.Listener.Metadata == nil {
@ -189,10 +245,11 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
})
}
routerOpts := []chain.RouterOption{
routerOpts = []chain.RouterOption{
chain.RetriesRouterOption(cfg.Handler.Retries),
// chain.TimeoutRouterOption(10*time.Second),
chain.TimeoutRouterOption(dialTimeout),
chain.InterfaceRouterOption(ifce),
chain.NetnsRouterOption(netnsOut),
chain.SockOptsRouterOption(sockOpts),
chain.ResolverRouterOption(registry.ResolverRegistry().Get(cfg.Resolver)),
chain.HostMapperRouterOption(registry.HostsRegistry().Get(cfg.Hosts)),
@ -204,27 +261,28 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
chain.ChainRouterOption(chainGroup(cfg.Handler.Chain, cfg.Handler.ChainGroup)),
)
}
router := chain.NewRouter(routerOpts...)
var h handler.Handler
if rf := registry.HandlerRegistry().Get(cfg.Handler.Type); rf != nil {
h = rf(
handler.RouterOption(router),
handler.RouterOption(chain.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.TLSConfigOption(tlsConfig),
handler.RateLimiterOption(registry.RateLimiterRegistry().Get(cfg.RLimiter)),
handler.TrafficLimiterOption(registry.TrafficLimiterRegistry().Get(cfg.Handler.Limiter)),
handler.ObserverOption(registry.ObserverRegistry().Get(cfg.Handler.Observer)),
handler.LoggerOption(handlerLogger),
handler.ServiceOption(cfg.Name),
handler.NetnsOption(netnsIn),
)
} else {
return nil, fmt.Errorf("unregistered handler: %s", cfg.Handler.Type)
return nil, fmt.Errorf("unknown handler: %s", cfg.Handler.Type)
}
if forwarder, ok := h.(handler.Forwarder); ok {
hop, err := parseForwarder(cfg.Forwarder)
hop, err := parseForwarder(cfg.Forwarder, log)
if err != nil {
return nil, err
}
@ -247,6 +305,9 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
xservice.PostUpOption(postUp),
xservice.PostDownOption(postDown),
xservice.RecordersOption(recorders...),
xservice.StatsOption(pStats),
xservice.ObserverOption(registry.ObserverRegistry().Get(cfg.Observer)),
xservice.ObservePeriodOption(observePeriod),
xservice.LoggerOption(serviceLogger),
)
@ -254,11 +315,19 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
return s, nil
}
func parseForwarder(cfg *config.ForwarderConfig) (hop.Hop, error) {
func parseForwarder(cfg *config.ForwarderConfig, log logger.Logger) (hop.Hop, error) {
if cfg == nil {
return nil, nil
}
hopName := cfg.Hop
if hopName == "" {
hopName = cfg.Name
}
if hopName != "" {
return registry.HopRegistry().Get(hopName), nil
}
hc := config.HopConfig{
Name: cfg.Name,
Selector: cfg.Selector,
@ -274,26 +343,42 @@ func parseForwarder(cfg *config.ForwarderConfig) (hop.Hop, error) {
if i > 0 {
name = fmt.Sprintf("%s-%d", node.Name, i)
}
filter := node.Filter
if filter == nil {
if node.Protocol != "" || node.Host != "" || node.Path != "" {
filter = &config.NodeFilterConfig{
Protocol: node.Protocol,
Host: node.Host,
Path: node.Path,
}
}
}
httpCfg := node.HTTP
if node.Auth != nil {
if httpCfg == nil {
httpCfg = &config.HTTPNodeConfig{}
}
if httpCfg.Auth == nil {
httpCfg.Auth = node.Auth
}
}
hc.Nodes = append(hc.Nodes, &config.NodeConfig{
Name: name,
Addr: addr,
Host: node.Host,
Network: node.Network,
Protocol: node.Protocol,
Path: node.Path,
Bypass: node.Bypass,
Bypasses: node.Bypasses,
HTTP: node.HTTP,
Filter: filter,
HTTP: httpCfg,
TLS: node.TLS,
Auth: node.Auth,
Metadata: node.Metadata,
})
}
}
}
if len(hc.Nodes) > 0 {
return hop_parser.ParseHop(&hc)
}
return registry.HopRegistry().Get(hc.Name), nil
return hop_parser.ParseHop(&hc, log)
}
func chainGroup(name string, group *config.ChainGroupConfig) chain.Chainer {

View File

@ -44,9 +44,17 @@ func (c *directConnector) Connect(ctx context.Context, _ net.Conn, network, addr
return nil, err
}
var localAddr, remoteAddr string
if addr := conn.LocalAddr(); addr != nil {
localAddr = addr.String()
}
if addr := conn.RemoteAddr(); addr != nil {
remoteAddr = addr.String()
}
log := c.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"remote": remoteAddr,
"local": localAddr,
"network": network,
"address": address,
})

View File

@ -69,6 +69,10 @@ func (c *udpRelayConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
if err = socksAddr.ParseFrom(addr.String()); err != nil {
return
}
if socksAddr.Host == "" {
socksAddr.Type = gosocks5.AddrIPv4
socksAddr.Host = "127.0.0.1"
}
header := gosocks5.UDPHeader{
Addr: &socksAddr,

View File

@ -130,6 +130,10 @@ func (c *socks5Connector) Connect(ctx context.Context, conn net.Conn, network, a
log.Error(err)
return nil, err
}
if addr.Host == "" {
addr.Type = gosocks5.AddrIPv4
addr.Host = "127.0.0.1"
}
req := gosocks5.NewRequest(gosocks5.CmdConnect, &addr)
log.Trace(req)
@ -201,12 +205,12 @@ func (c *socks5Connector) relayUDP(ctx context.Context, conn net.Conn, addr net.
}
log.Trace(reply)
log.Debugf("bind on: %v", reply.Addr)
if reply.Rep != gosocks5.Succeeded {
return nil, errors.New("get socks5 UDP tunnel failure")
}
log.Debugf("bind on: %v", reply.Addr)
cc, err := opts.NetDialer.Dial(ctx, "udp", reply.Addr.String())
if err != nil {
return nil, err

View File

@ -27,7 +27,8 @@ func (c *tunnelConnector) Bind(ctx context.Context, conn net.Conn, network, addr
"endpoint": endpoint,
"tunnel": c.md.tunnelID.String(),
})
log.Infof("create tunnel on %s/%s OK, tunnel=%s, connector=%s", addr, network, c.md.tunnelID.String(), cid)
log.Infof("create tunnel on %s/%s OK, tunnel=%s, connector=%s, weight=%d",
addr, network, c.md.tunnelID.String(), cid, cid.Weight())
session, err := mux.ServerSession(conn, c.md.muxCfg)
if err != nil {
@ -72,7 +73,7 @@ func (c *tunnelConnector) initTunnel(conn net.Conn, network, address string) (ad
req.Features = append(req.Features, af) // dst address
req.Features = append(req.Features, &relay.TunnelFeature{
ID: c.md.tunnelID.ID(),
ID: c.md.tunnelID,
})
if _, err = req.WriteTo(conn); err != nil {
return
@ -100,7 +101,7 @@ func (c *tunnelConnector) initTunnel(conn net.Conn, network, address string) (ad
}
case relay.FeatureTunnel:
if feature, _ := f.(*relay.TunnelFeature); feature != nil {
cid = relay.NewConnectorID(feature.ID[:])
cid = feature.ID
}
}
}

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/internal/ctx"
ctxvalue "github.com/go-gost/x/ctx"
"github.com/go-gost/x/registry"
)
@ -86,7 +86,7 @@ func (c *tunnelConnector) Connect(ctx context.Context, conn net.Conn, network, a
req.Features = append(req.Features, af) // dst address
req.Features = append(req.Features, &relay.TunnelFeature{
ID: c.md.tunnelID.ID(),
ID: c.md.tunnelID,
})
if _, err := req.WriteTo(conn); err != nil {

View File

@ -40,6 +40,10 @@ func (c *tunnelConnector) parseMetadata(md mdata.Metadata) (err error) {
c.md.tunnelID = relay.NewTunnelID(uuid[:])
}
if weight := mdutil.GetInt(md, "tunnel.weight"); weight > 0 {
c.md.tunnelID = c.md.tunnelID.SetWeight(uint8(weight))
}
c.md.muxCfg = &mux.Config{
Version: mdutil.GetInt(md, "mux.version"),
KeepAliveInterval: mdutil.GetDuration(md, "mux.keepaliveInterval"),

View File

@ -70,6 +70,19 @@ func (d *http2Dialer) Dial(ctx context.Context, address string, opts ...dialer.D
opt(&options)
}
{
// Check whether the connection is established properly
netd := options.NetDialer
if netd == nil {
netd = net_dialer.DefaultNetDialer
}
conn, err := netd.Dial(ctx, "tcp", address)
if err != nil {
return nil, err
}
conn.Close()
}
client = &http.Client{
Transport: &http.Transport{
TLSClientConfig: d.options.TLSConfig,
@ -81,17 +94,16 @@ func (d *http2Dialer) Dial(ctx context.Context, address string, opts ...dialer.D
return netd.Dial(ctx, network, addr)
},
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
MaxIdleConns: 16,
IdleConnTimeout: 30 * time.Second,
TLSHandshakeTimeout: 30 * time.Second,
ExpectContinueTimeout: 15 * time.Second,
},
}
d.clients[address] = client
}
var c net.Conn
c = &conn{
var c net.Conn = &conn{
localAddr: &net.TCPAddr{},
remoteAddr: raddr,
onClose: func() {

View File

@ -86,7 +86,7 @@ func (d *http3Dialer) Dial(ctx context.Context, addr string, opts ...dialer.Dial
return quic.DialEarly(context.Background(), udpConn.(net.PacketConn), udpAddr, tlsCfg, cfg)
},
QuicConfig: &quic.Config{
QUICConfig: &quic.Config{
KeepAlivePeriod: d.md.keepAlivePeriod,
HandshakeIdleTimeout: d.md.handshakeTimeout,
MaxIdleTimeout: d.md.maxIdleTimeout,

View File

@ -50,7 +50,7 @@ func (d *http3Dialer) parseMetadata(md mdata.Metadata) (err error) {
}
d.md.host = mdutil.GetString(md, "host")
if !md.IsExists(keepAlive) || mdutil.GetBool(md, keepAlive) {
if md == nil || !md.IsExists(keepAlive) || mdutil.GetBool(md, keepAlive) {
d.md.keepAlivePeriod = mdutil.GetDuration(md, keepAlivePeriod)
if d.md.keepAlivePeriod <= 0 {
d.md.keepAlivePeriod = 10 * time.Second

View File

@ -10,7 +10,6 @@ import (
md "github.com/go-gost/core/metadata"
"github.com/go-gost/x/registry"
"github.com/quic-go/quic-go"
"github.com/quic-go/quic-go/http3"
wt "github.com/quic-go/webtransport-go"
)
@ -74,33 +73,32 @@ func (d *wtDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOpt
path: d.md.path,
header: d.md.header,
dialer: &wt.Dialer{
RoundTripper: &http3.RoundTripper{
TLSClientConfig: d.options.TLSConfig,
Dial: func(ctx context.Context, adr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
// d.options.Logger.Infof("dial: %s, %s, %s", addr, adr, host)
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
TLSClientConfig: d.options.TLSConfig,
DialAddr: func(ctx context.Context, adr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlyConnection, error) {
// d.options.Logger.Infof("dial: %s, %s, %s", addr, adr, host)
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
udpConn, err := options.NetDialer.Dial(ctx, "udp", "")
if err != nil {
return nil, err
}
udpConn, err := options.NetDialer.Dial(ctx, "udp", "")
if err != nil {
return nil, err
}
return quic.DialEarly(ctx, udpConn.(net.PacketConn), udpAddr, tlsCfg, cfg)
},
QuicConfig: &quic.Config{
KeepAlivePeriod: d.md.keepAlivePeriod,
HandshakeIdleTimeout: d.md.handshakeTimeout,
MaxIdleTimeout: d.md.maxIdleTimeout,
/*
Versions: []quic.VersionNumber{
quic.Version1,
},
*/
MaxIncomingStreams: int64(d.md.maxStreams),
},
return quic.DialEarly(ctx, udpConn.(net.PacketConn), udpAddr, tlsCfg, cfg)
},
QUICConfig: &quic.Config{
KeepAlivePeriod: d.md.keepAlivePeriod,
HandshakeIdleTimeout: d.md.handshakeTimeout,
MaxIdleTimeout: d.md.maxIdleTimeout,
/*
Versions: []quic.VersionNumber{
quic.Version1,
},
*/
MaxIncomingStreams: int64(d.md.maxStreams),
EnableDatagrams: true,
},
},
}

View File

@ -40,7 +40,7 @@ func (d *wtDialer) parseMetadata(md mdata.Metadata) (err error) {
d.md.path = defaultPath
}
if !md.IsExists(keepAlive) || mdutil.GetBool(md, keepAlive) {
if md == nil || !md.IsExists(keepAlive) || mdutil.GetBool(md, keepAlive) {
d.md.keepAlivePeriod = mdutil.GetDuration(md, keepAlivePeriod)
if d.md.keepAlivePeriod <= 0 {
d.md.keepAlivePeriod = 10 * time.Second

View File

@ -115,7 +115,7 @@ func (d *icmpDialer) initSession(ctx context.Context, addr net.Addr, conn net.Pa
}
tlsCfg := d.options.TLSConfig
tlsCfg.NextProtos = []string{"http/3", "quic/v1"}
tlsCfg.NextProtos = []string{"h3", "quic/v1"}
session, err := quic.DialEarly(ctx, conn, addr, tlsCfg, quicConfig)
if err != nil {

View File

@ -149,7 +149,9 @@ func (d *kcpDialer) initSession(ctx context.Context, addr net.Addr, conn net.Pac
smuxConfig.Version = config.SmuxVer
smuxConfig.MaxReceiveBuffer = config.SmuxBuf
smuxConfig.MaxStreamBuffer = config.StreamBuf
smuxConfig.KeepAliveInterval = time.Duration(config.KeepAlive) * time.Second
if config.KeepAlive > 0 {
smuxConfig.KeepAliveInterval = time.Duration(config.KeepAlive) * time.Second
}
var cc net.Conn = kcpconn
if !config.NoComp {
cc = kcp_util.CompStreamConn(kcpconn)

View File

@ -49,7 +49,12 @@ func (d *kcpDialer) parseMetadata(md mdata.Metadata) (err error) {
d.md.config.KeepAlive = mdutil.GetInt(md, "kcp.keepalive")
d.md.config.Interval = mdutil.GetInt(md, "kcp.interval")
d.md.config.MTU = mdutil.GetInt(md, "kcp.mtu")
d.md.config.RcvWnd = mdutil.GetInt(md, "kcp.rcvwnd")
d.md.config.SndWnd = mdutil.GetInt(md, "kcp.sndwnd")
d.md.config.SmuxVer = mdutil.GetInt(md, "kcp.smuxver")
d.md.config.SmuxBuf = mdutil.GetInt(md, "kcp.smuxbuf")
d.md.config.StreamBuf = mdutil.GetInt(md, "kcp.streambuf")
d.md.config.NoComp = mdutil.GetBool(md, "kcp.nocomp")
d.md.handshakeTimeout = mdutil.GetDuration(md, handshakeTimeout)
return

View File

@ -18,6 +18,7 @@ import (
type obfsHTTPConn struct {
net.Conn
host string
path string
rbuf bytes.Buffer
wbuf bytes.Buffer
headerDrained bool

View File

@ -2,6 +2,7 @@ package http
import (
"context"
"crypto/tls"
"net"
"github.com/go-gost/core/dialer"
@ -12,11 +13,13 @@ import (
func init() {
registry.DialerRegistry().Register("ohttp", NewDialer)
registry.DialerRegistry().Register("ohttps", NewDialer)
}
type obfsHTTPDialer struct {
md metadata
logger logger.Logger
tlsEnabled bool
md metadata
logger logger.Logger
}
func NewDialer(opts ...dialer.Option) dialer.Dialer {
@ -30,6 +33,18 @@ func NewDialer(opts ...dialer.Option) dialer.Dialer {
}
}
func NewTLSDialer(opts ...dialer.Option) dialer.Dialer {
options := &dialer.Options{}
for _, opt := range opts {
opt(options)
}
return &obfsHTTPDialer{
tlsEnabled: true,
logger: options.Logger,
}
}
func (d *obfsHTTPDialer) Init(md md.Metadata) (err error) {
return d.parseMetadata(md)
}
@ -59,9 +74,16 @@ func (d *obfsHTTPDialer) Handshake(ctx context.Context, conn net.Conn, options .
host = opts.Addr
}
if d.tlsEnabled {
conn = tls.Client(conn, &tls.Config{
ServerName: host,
})
}
return &obfsHTTPConn{
Conn: conn,
host: host,
path: d.md.path,
header: d.md.header,
logger: d.logger,
}, nil

View File

@ -7,24 +7,29 @@ import (
mdutil "github.com/go-gost/core/metadata/util"
)
const (
defaultPath = "/"
)
type metadata struct {
host string
path string
header http.Header
}
func (d *obfsHTTPDialer) parseMetadata(md mdata.Metadata) (err error) {
const (
header = "header"
host = "host"
)
d.md.host = mdutil.GetString(md, "obfs.host", "host")
d.md.path = mdutil.GetString(md, "obfs.path", "path")
if d.md.path == "" {
d.md.path = defaultPath
}
if m := mdutil.GetStringMapString(md, header); len(m) > 0 {
if m := mdutil.GetStringMapString(md, "obfs.header", "header"); len(m) > 0 {
h := http.Header{}
for k, v := range m {
h.Add(k, v)
}
d.md.header = h
}
d.md.host = mdutil.GetString(md, host)
return
}

View File

@ -114,7 +114,7 @@ func (d *quicDialer) initSession(ctx context.Context, addr net.Addr, conn net.Pa
}
tlsCfg := d.options.TLSConfig
tlsCfg.NextProtos = []string{"http/3", "quic/v1"}
tlsCfg.NextProtos = []string{"h3", "quic/v1"}
session, err := quic.DialEarly(ctx, conn, addr, tlsCfg, quicConfig)
if err != nil {

View File

@ -31,7 +31,7 @@ func (d *quicDialer) parseMetadata(md mdata.Metadata) (err error) {
d.md.cipherKey = []byte(key)
}
if !md.IsExists(keepAlive) || mdutil.GetBool(md, keepAlive) {
if md == nil || !md.IsExists(keepAlive) || mdutil.GetBool(md, keepAlive) {
d.md.keepAlivePeriod = mdutil.GetDuration(md, keepAlivePeriod)
if d.md.keepAlivePeriod <= 0 {
d.md.keepAlivePeriod = 10 * time.Second

View File

@ -1,11 +1,14 @@
package ssh
import (
"fmt"
"os"
"time"
mdata "github.com/go-gost/core/metadata"
mdutil "github.com/go-gost/core/metadata/util"
"github.com/mitchellh/go-homedir"
"github.com/zalando/go-keyring"
"golang.org/x/crypto/ssh"
)
@ -20,21 +23,35 @@ type metadata struct {
func (d *sshDialer) parseMetadata(md mdata.Metadata) (err error) {
const (
handshakeTimeout = "handshakeTimeout"
privateKeyFile = "privateKeyFile"
passphrase = "passphrase"
handshakeTimeout = "handshakeTimeout"
privateKeyFile = "privateKeyFile"
passphrase = "passphrase"
passphraseFromKeyring = "passphraseFromKeyring"
)
if key := mdutil.GetString(md, privateKeyFile); key != "" {
key, err = homedir.Expand(key)
if err != nil {
return err
}
data, err := os.ReadFile(key)
if err != nil {
return err
}
if pp := mdutil.GetString(md, passphrase); pp != "" {
d.md.signer, err = ssh.ParsePrivateKeyWithPassphrase(data, []byte(pp))
var pp string
if mdutil.GetBool(md, passphraseFromKeyring) {
pp, err = keyring.Get(fmt.Sprintf("SSH %s", key), d.options.Auth.Username())
if err != nil {
return fmt.Errorf("unable to get secret(%s) from keyring: %w", key, err)
}
} else {
pp = mdutil.GetString(md, passphrase)
}
if pp == "" {
d.md.signer, err = ssh.ParsePrivateKey(data)
} else {
d.md.signer, err = ssh.ParsePrivateKeyWithPassphrase(data, []byte(pp))
}
if err != nil {
return err

View File

@ -1,11 +1,14 @@
package sshd
import (
"fmt"
"os"
"time"
mdata "github.com/go-gost/core/metadata"
mdutil "github.com/go-gost/core/metadata/util"
"github.com/mitchellh/go-homedir"
"github.com/zalando/go-keyring"
"golang.org/x/crypto/ssh"
)
@ -26,12 +29,24 @@ func (d *sshdDialer) parseMetadata(md mdata.Metadata) (err error) {
)
if key := mdutil.GetString(md, privateKeyFile); key != "" {
key, err = homedir.Expand(key)
if err != nil {
return err
}
data, err := os.ReadFile(key)
if err != nil {
return err
}
pp := mdutil.GetString(md, passphrase)
var pp string
if mdutil.GetBool(md, "passphraseFromKeyring") {
pp, err = keyring.Get(fmt.Sprintf("SSH %s", key), key)
if err != nil {
return fmt.Errorf("unable to get secret(%s) from keyring: %w", key, err)
}
} else {
pp = mdutil.GetString(md, passphrase)
}
if pp == "" {
d.md.signer, err = ssh.ParsePrivateKey(data)
} else {

48
dialer/wg/dialer.go Normal file
View File

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

23
dialer/wg/metadata.go Normal file
View File

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

120
go.mod
View File

@ -1,117 +1,121 @@
module github.com/go-gost/x
go 1.21
go 1.22
toolchain go1.22.2
require (
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
github.com/gin-contrib/cors v1.3.1
github.com/gin-contrib/cors v1.5.0
github.com/gin-gonic/gin v1.9.1
github.com/go-gost/core v0.0.0-20231119081403-abc73f2ca2b7
github.com/go-gost/core v0.0.0-20240704150322-30cc92870515
github.com/go-gost/gosocks4 v0.0.1
github.com/go-gost/gosocks5 v0.4.0
github.com/go-gost/plugin v0.0.0-20231119084331-d49a1cb23b3b
github.com/go-gost/relay v0.4.1-0.20230916134211-828f314ddfe7
github.com/go-gost/gosocks5 v0.4.2
github.com/go-gost/plugin v0.0.0-20240103125338-9c84e29cb81a
github.com/go-gost/relay v0.5.0
github.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451
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/uuid v1.4.0
github.com/gorilla/websocket v1.5.0
github.com/miekg/dns v1.1.56
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.1
github.com/miekg/dns v1.1.57
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/prometheus/client_golang v1.17.0
github.com/quic-go/quic-go v0.39.0
github.com/quic-go/webtransport-go v0.6.0
github.com/refraction-networking/utls v1.5.4
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/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/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
github.com/spf13/viper v1.14.0
github.com/spf13/viper v1.18.2
github.com/vishvananda/netlink v1.1.0
github.com/xtaci/kcp-go/v5 v5.6.1
github.com/vishvananda/netns v0.0.4
github.com/xtaci/kcp-go/v5 v5.6.5
github.com/xtaci/smux v1.5.24
github.com/xtaci/tcpraw v1.2.25
github.com/yl2chen/cidranger v1.0.2
golang.org/x/crypto v0.14.0
golang.org/x/net v0.17.0
golang.org/x/sys v0.13.0
golang.org/x/time v0.3.0
github.com/zalando/go-keyring v0.2.4
golang.org/x/crypto v0.24.0
golang.org/x/net v0.26.0
golang.org/x/sys v0.21.0
golang.org/x/time v0.5.0
golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478
google.golang.org/grpc v1.59.0
google.golang.org/protobuf v1.31.0
google.golang.org/grpc v1.64.0
google.golang.org/protobuf v1.34.2
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1
)
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.9.1 // indirect
github.com/bytedance/sonic v1.10.2 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/coreos/go-iptables v0.5.0 // indirect
github.com/danieljoos/wincred v1.2.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // 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.14.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/go-playground/validator/v10 v10.16.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/google/pprof v0.0.0-20230912144702-c363fe2c2ed8 // indirect
github.com/google/pprof v0.0.0-20240528025155-186aa0362fba // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid v1.3.1 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/klauspost/reedsolomon v1.9.9 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/klauspost/reedsolomon v1.11.8 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 // 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.12.0 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/onsi/ginkgo/v2 v2.19.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // 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.4.1-0.20230718164431-9a2bf3000d16 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.11.1 // indirect
github.com/prometheus/client_model v0.5.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/qtls-go1-20 v0.3.4 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/spf13/afero v1.9.3 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
github.com/templexxx/cpu v0.0.7 // indirect
github.com/templexxx/xorsimd v0.4.1 // indirect
github.com/tjfoc/gmsm v1.3.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/templexxx/cpu v0.1.0 // indirect
github.com/templexxx/xorsimd v0.4.2 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect
go.uber.org/mock v0.3.0 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/mod v0.12.0 // indirect
golang.org/x/text v0.13.0 // indirect
golang.org/x/tools v0.13.0 // indirect
go.uber.org/mock v0.4.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.6.0 // indirect
golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 // indirect
golang.org/x/mod v0.18.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/tools v0.22.0 // indirect
golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

637
go.sum
View File

@ -1,265 +1,161 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/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=
github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ=
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.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE=
github.com/bytedance/sonic v1.10.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/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/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/coreos/go-iptables v0.5.0 h1:mw6SAibtHKZcNzAsOxjoHIG0gy5YFHhypWSSNc6EjbQ=
github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE=
github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gin-contrib/cors v1.3.1 h1:doAsuITavI4IOcd0Y19U4B+O0dNWihRyX//nn4sEmgA=
github.com/gin-contrib/cors v1.3.1/go.mod h1:jjEJ4268OPZUcU7k9Pm653S7lXUGcqMADzFA61xsmDk=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/cors v1.5.0 h1:DgGKV7DDoOn36DFkNtbHrjoRiT5ExCe+PC9/xp7aKvk=
github.com/gin-contrib/cors v1.5.0/go.mod h1:TvU7MAZ3EwrPLI2ztzTt3tqgvBCq+wn8WpZmfADjupI=
github.com/gin-contrib/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.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
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-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gost/core v0.0.0-20231119081403-abc73f2ca2b7 h1:fxVUlZANqPApygO7lT8bYySyajiCFA62bDiNorral1w=
github.com/go-gost/core v0.0.0-20231119081403-abc73f2ca2b7/go.mod h1:ndkgWVYRLwupVaFFWv8ML1Nr8tD3xhHK245PLpUDg4E=
github.com/go-gost/core v0.0.0-20240704150322-30cc92870515 h1:i/zcDZtz00hcmRosvJgXmgJsdc4bC32PAvt2+8MUOEg=
github.com/go-gost/core v0.0.0-20240704150322-30cc92870515/go.mod h1:QmVAZIXIYBsX44Vehwug5RFnG2K3/Hz/uu/Y4QVhAY0=
github.com/go-gost/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.0 h1:EIrOEkpJez4gwHrMa33frA+hHXJyevjp47thpMQsJzI=
github.com/go-gost/gosocks5 v0.4.0/go.mod h1:1G6I7HP7VFVxveGkoK8mnprnJqSqJjdcASKsdUn4Pp4=
github.com/go-gost/plugin v0.0.0-20231119084331-d49a1cb23b3b h1:ZmnYutflq+KOZK+Px5RDckorDSxTYlkT4aQbjTC8/C4=
github.com/go-gost/plugin v0.0.0-20231119084331-d49a1cb23b3b/go.mod h1:qXr2Zm9Ex2ATqnWuNUzVZqySPMnuIihvblYZt4MlZLw=
github.com/go-gost/relay v0.4.1-0.20230916134211-828f314ddfe7 h1:qAG1OyjvdA5h221CfFSS3J359V3d2E7dJWyP29QoDSI=
github.com/go-gost/relay v0.4.1-0.20230916134211-828f314ddfe7/go.mod h1:lcX+23LCQ3khIeASBo+tJ/WbwXFO32/N5YN6ucuYTG8=
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.0.0-20240103125338-9c84e29cb81a h1:ME7P1Brcg4C640DSPqlvQr7JuvvQfJ8QpmS3yCFlK3A=
github.com/go-gost/plugin v0.0.0-20240103125338-9c84e29cb81a/go.mod h1:qXr2Zm9Ex2ATqnWuNUzVZqySPMnuIihvblYZt4MlZLw=
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.0.2-0.20220408131628-aac992c27451 h1:xj8gUZGYO3nb5+6Bjw9+tsFkA9sYynrOvDvvC4uDV2I=
github.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451/go.mod h1:/9QfdewqmHdaE362Hv5nDaSWLx3pCmtD870d6GaquXs=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
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.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
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.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
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.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE=
github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/go-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 v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
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=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
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 v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/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/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20230912144702-c363fe2c2ed8 h1:gpptm606MZYGaMHMsB4Srmb6EbW/IVHnt04rcMXnkBQ=
github.com/google/pprof v0.0.0-20230912144702-c363fe2c2ed8/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/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/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/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/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
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/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s=
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/klauspost/reedsolomon v1.9.9 h1:qCL7LZlv17xMixl55nq2/Oa1Y86nfO8EqDfv2GHND54=
github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/reedsolomon v1.11.8 h1:s8RpUW5TK4hjr+djiOpbZJB4ksx+TdYbRH7vHQpwPOY=
github.com/klauspost/reedsolomon v1.11.8/go.mod h1:4bXRN+cVzMdml6ti7qLouuYi32KHJ5MGv0Qd8a47h6A=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE=
github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 h1:ULR/QWMgcgRiZLUjSSJMU+fW+RDMstRdmnDWj9Q+AsA=
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
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.12.0 h1:UIVDowFPwpg6yMUpPjGkYvf06K3RAiJXUhCxEwQVHRI=
github.com/onsi/ginkgo/v2 v2.12.0/go.mod h1:ZNEzXISYlqpb8S36iN71ifqLi3vVD1rVJGvWRCJOUpQ=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
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/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 v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
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/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=
@ -272,34 +168,34 @@ github.com/pires/go-proxyproto v0.7.0 h1:IukmRewDQFWC7kfnb66CSomk2q/seBuilHBYFwy
github.com/pires/go-proxyproto v0.7.0/go.mod h1:Vz/1JPY/OACxWGQNIRY2BeyDmpoaWmEP40O9LbuiFR4=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q=
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
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.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM=
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY=
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI=
github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY=
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/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/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg=
github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=
github.com/quic-go/quic-go v0.39.0 h1:AgP40iThFMY0bj8jGxROhw3S0FMGa8ryqsmi9tBH3So=
github.com/quic-go/quic-go v0.39.0/go.mod h1:T09QsDQWjLiQ74ZmacDfqZmhY/NLnw5BC40MANNNZ1Q=
github.com/quic-go/webtransport-go v0.6.0 h1:CvNsKqc4W2HljHJnoT+rMmbRJybShZ0YPFDD3NxaZLY=
github.com/quic-go/webtransport-go v0.6.0/go.mod h1:9KjU4AEBqEQidGHNDkZrb8CAa1abRaosM2yGOyiikEc=
github.com/refraction-networking/utls v1.5.4/go.mod h1:SPuDbBmgLGp8s+HLNc83FuavwZCFoMmExj+ltUHiHUw=
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/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.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4=
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
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=
@ -308,52 +204,49 @@ github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
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/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk=
github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
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.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU=
github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As=
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/templexxx/cpu v0.0.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
github.com/templexxx/cpu v0.0.7 h1:pUEZn8JBy/w5yzdYWgx+0m0xL9uk6j4K91C5kOViAzo=
github.com/templexxx/cpu v0.0.7/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
github.com/templexxx/xorsimd v0.4.1 h1:iUZcywbOYDRAZUasAs2eSCUW8eobuZDy0I9FJiORkVg=
github.com/templexxx/xorsimd v0.4.1/go.mod h1:W+ffZz8jJMH2SXwuKu9WhygqBMbFnp14G2fqEr8qaNo=
github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
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/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 v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/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 h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/xtaci/kcp-go/v5 v5.6.1 h1:Pwn0aoeNSPF9dTS7IgiPXn0HEtaIlVb6y5UKWPsx8bI=
github.com/xtaci/kcp-go/v5 v5.6.1/go.mod h1:W3kVPyNYwZ06p79dNwFWQOVFrdcBpDBsdyvK8moQrYo=
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
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=
@ -362,366 +255,127 @@ 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=
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=
go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
golang.org/x/arch v0.0.0-20190909030613-46d78d1859ac/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
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/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.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.6.0 h1:S0JTfE48HbRj80+4tbvZDYsJ3tGv6BUU3XxyZ7CirAc=
golang.org/x/arch v0.6.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-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-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/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/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/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-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/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.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/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-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/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-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/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.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.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.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
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/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=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200425043458-8463f397d07c/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200808161706-5bf02b21f123/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/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=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b h1:ZlWIi1wSK56/8hn4QcBp/j9M7Gt3U/3hZw3mC7vDICo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 h1:9Xyg6I9IWQZhRVfCWjKK+l6kI0jHcPesVlMnT//aHNo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
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.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
@ -729,19 +383,10 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYs
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

View File

@ -11,6 +11,7 @@ import (
md "github.com/go-gost/core/metadata"
"github.com/go-gost/gosocks4"
"github.com/go-gost/gosocks5"
ctxvalue "github.com/go-gost/x/ctx"
netpkg "github.com/go-gost/x/internal/net"
"github.com/go-gost/x/registry"
)
@ -79,6 +80,7 @@ func (h *autoHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler
log := h.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"sid": ctxvalue.SidFromContext(ctx),
})
if log.IsLevelEnabled(logger.DebugLevel) {

View File

@ -71,7 +71,10 @@ func (h *dnsHandler) Init(md md.Metadata) (err error) {
for i, addr := range h.md.dns {
nodes = append(nodes, chain.NewNode(fmt.Sprintf("target-%d", i), addr))
}
h.hop = xhop.NewHop(xhop.NodeOption(nodes...))
h.hop = xhop.NewHop(
xhop.NodeOption(nodes...),
xhop.LoggerOption(log),
)
}
var nodes []*chain.Node

View File

@ -2,6 +2,7 @@ package dns
import (
"net"
"strings"
"time"
mdata "github.com/go-gost/core/metadata"
@ -45,7 +46,14 @@ func (h *dnsHandler) parseMetadata(md mdata.Metadata) (err error) {
if sip != "" {
h.md.clientIP = net.ParseIP(sip)
}
h.md.dns = mdutil.GetStrings(md, dns)
for _, v := range strings.Split(mdutil.GetString(md, dns), ",") {
v = strings.TrimSpace(v)
if v == "" {
continue
}
h.md.dns = append(h.md.dns, v)
}
h.md.bufferSize = mdutil.GetInt(md, bufferSize)
if h.md.bufferSize <= 0 {
h.md.bufferSize = defaultBufferSize

View File

@ -73,6 +73,7 @@ func (h *fileHandler) 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 {
w.Header().Set("WWW-Authenticate", "Basic")
w.WriteHeader(http.StatusUnauthorized)
return
}

View File

@ -21,7 +21,7 @@ import (
"github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata"
"github.com/go-gost/x/config"
ctxvalue "github.com/go-gost/x/internal/ctx"
ctxvalue "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/util/forward"
@ -247,24 +247,34 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, remot
})
log.Debugf("find node for host %s -> %s(%s)", req.Host, target.Name, target.Addr)
if auther := target.Options().Auther; auther != nil {
username, password, _ := req.BasicAuth()
id, ok := auther.Authenticate(ctx, username, password)
if !ok {
resp.StatusCode = http.StatusUnauthorized
resp.Header.Set("WWW-Authenticate", "Basic")
log.Warnf("node %s(%s) 401 unauthorized", target.Name, target.Addr)
return resp.Write(rw)
}
ctx = ctxvalue.ContextWithClientID(ctx, ctxvalue.ClientID(id))
}
if httpSettings := target.Options().HTTP; httpSettings != nil {
if auther := httpSettings.Auther; auther != nil {
username, password, _ := req.BasicAuth()
id, ok := auther.Authenticate(ctx, username, password)
if !ok {
resp.StatusCode = http.StatusUnauthorized
resp.Header.Set("WWW-Authenticate", "Basic")
log.Warnf("node %s(%s) 401 unauthorized", target.Name, target.Addr)
return resp.Write(rw)
}
ctx = ctxvalue.ContextWithClientID(ctx, ctxvalue.ClientID(id))
}
if httpSettings.Host != "" {
req.Host = httpSettings.Host
}
for k, v := range httpSettings.Header {
req.Header.Set(k, v)
}
for _, re := range httpSettings.Rewrite {
if re.Pattern.MatchString(req.URL.Path) {
if s := re.Pattern.ReplaceAllString(req.URL.Path, re.Replacement); s != "" {
req.URL.Path = s
break
}
}
}
}
cc, err = h.router.Dial(ctx, "tcp", target.Addr)

View File

@ -22,7 +22,7 @@ import (
mdata "github.com/go-gost/core/metadata"
mdutil "github.com/go-gost/core/metadata/util"
"github.com/go-gost/x/config"
ctxvalue "github.com/go-gost/x/internal/ctx"
ctxvalue "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/proxyproto"
@ -248,24 +248,33 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, remot
})
log.Debugf("find node for host %s -> %s(%s)", req.Host, target.Name, target.Addr)
if auther := target.Options().Auther; auther != nil {
username, password, _ := req.BasicAuth()
id, ok := auther.Authenticate(ctx, username, password)
if !ok {
resp.StatusCode = http.StatusUnauthorized
resp.Header.Set("WWW-Authenticate", "Basic")
log.Warnf("node %s(%s) 401 unauthorized", target.Name, target.Addr)
return resp.Write(rw)
}
ctx = ctxvalue.ContextWithClientID(ctx, ctxvalue.ClientID(id))
}
if httpSettings := target.Options().HTTP; httpSettings != nil {
if auther := httpSettings.Auther; auther != nil {
username, password, _ := req.BasicAuth()
id, ok := auther.Authenticate(ctx, username, password)
if !ok {
resp.StatusCode = http.StatusUnauthorized
resp.Header.Set("WWW-Authenticate", "Basic")
log.Warnf("node %s(%s) 401 unauthorized", target.Name, target.Addr)
return resp.Write(rw)
}
ctx = ctxvalue.ContextWithClientID(ctx, ctxvalue.ClientID(id))
}
if httpSettings.Host != "" {
req.Host = httpSettings.Host
}
for k, v := range httpSettings.Header {
req.Header.Set(k, v)
}
for _, re := range httpSettings.Rewrite {
if re.Pattern.MatchString(req.URL.Path) {
if s := re.Pattern.ReplaceAllString(req.URL.Path, re.Replacement); s != "" {
req.URL.Path = s
break
}
}
}
}
cc, err = h.router.Dial(ctx, "tcp", target.Addr)

View File

@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"hash/crc32"
"io"
"net"
"net/http"
"net/http/httputil"
@ -19,12 +20,15 @@ import (
"github.com/asaskevich/govalidator"
"github.com/go-gost/core/chain"
"github.com/go-gost/core/handler"
"github.com/go-gost/core/limiter/traffic"
traffic "github.com/go-gost/core/limiter/traffic"
"github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata"
ctxvalue "github.com/go-gost/x/internal/ctx"
"github.com/go-gost/core/observer/stats"
ctxvalue "github.com/go-gost/x/ctx"
netpkg "github.com/go-gost/x/internal/net"
"github.com/go-gost/x/limiter/traffic/wrapper"
stats_util "github.com/go-gost/x/internal/util/stats"
traffic_wrapper "github.com/go-gost/x/limiter/traffic/wrapper"
stats_wrapper "github.com/go-gost/x/observer/stats/wrapper"
"github.com/go-gost/x/registry"
)
@ -36,6 +40,8 @@ type httpHandler struct {
router *chain.Router
md metadata
options handler.Options
stats *stats_util.HandlerStats
cancel context.CancelFunc
}
func NewHandler(opts ...handler.Option) handler.Handler {
@ -46,6 +52,7 @@ func NewHandler(opts ...handler.Option) handler.Handler {
return &httpHandler{
options: options,
stats: stats_util.NewHandlerStats(options.Service),
}
}
@ -59,6 +66,13 @@ func (h *httpHandler) Init(md md.Metadata) error {
h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger))
}
ctx, cancel := context.WithCancel(context.Background())
h.cancel = cancel
if h.options.Observer != nil {
go h.observeStats(ctx)
}
return nil
}
@ -71,6 +85,7 @@ func (h *httpHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler
log := h.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"sid": ctxvalue.SidFromContext(ctx),
})
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
defer func() {
@ -93,6 +108,13 @@ func (h *httpHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler
return h.handleRequest(ctx, conn, req, log)
}
func (h *httpHandler) Close() error {
if h.cancel != nil {
h.cancel()
}
return nil
}
func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *http.Request, log logger.Logger) error {
if !req.URL.IsAbs() && govalidator.IsDNSName(req.Host) {
req.URL.Scheme = "http"
@ -127,7 +149,7 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
fields := map[string]any{
"dst": addr,
}
if u, _, _ := h.basicProxyAuth(req.Header.Get("Proxy-Authorization"), log); u != "" {
if u, _, _ := h.basicProxyAuth(req.Header.Get("Proxy-Authorization")); u != "" {
fields["user"] = u
}
log = log.WithFields(fields)
@ -139,14 +161,19 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
log.Debugf("%s >> %s", conn.RemoteAddr(), addr)
resp := &http.Response{
ProtoMajor: 1,
ProtoMinor: 1,
Header: h.md.header,
ProtoMajor: 1,
ProtoMinor: 1,
Header: h.md.header,
ContentLength: -1,
}
if resp.Header == nil {
resp.Header = http.Header{}
}
if resp.Header.Get("Proxy-Agent") == "" {
resp.Header.Set("Proxy-Agent", h.md.proxyAgent)
}
clientID, ok := h.authenticate(ctx, conn, req, resp, log)
if !ok {
return nil
@ -201,32 +228,35 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
}
defer cc.Close()
if req.Method == http.MethodConnect {
resp.StatusCode = http.StatusOK
resp.Status = "200 Connection established"
if log.IsLevelEnabled(logger.TraceLevel) {
dump, _ := httputil.DumpResponse(resp, false)
log.Trace(string(dump))
}
if err = resp.Write(conn); err != nil {
log.Error(err)
return err
}
} else {
req.Header.Del("Proxy-Connection")
if err = req.Write(cc); err != nil {
log.Error(err)
return err
}
}
rw := wrapper.WrapReadWriter(h.options.Limiter, conn, conn.RemoteAddr().String(),
rw := traffic_wrapper.WrapReadWriter(h.options.Limiter, conn,
traffic.NetworkOption(network),
traffic.AddrOption(addr),
traffic.ClientOption(clientID),
traffic.SrcOption(conn.RemoteAddr().String()),
)
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)
}
if req.Method != http.MethodConnect {
return h.handleProxy(rw, cc, req, log)
}
resp.StatusCode = http.StatusOK
resp.Status = "200 Connection established"
if log.IsLevelEnabled(logger.TraceLevel) {
dump, _ := httputil.DumpResponse(resp, false)
log.Trace(string(dump))
}
if err = resp.Write(rw); err != nil {
log.Error(err)
return err
}
start := time.Now()
log.Infof("%s <-> %s", conn.RemoteAddr(), addr)
@ -238,6 +268,49 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
return nil
}
func (h *httpHandler) handleProxy(rw, cc io.ReadWriter, req *http.Request, log logger.Logger) (err error) {
req.Header.Del("Proxy-Connection")
if err = req.Write(cc); err != nil {
log.Error(err)
return err
}
ch := make(chan error, 1)
go func() {
ch <- netpkg.CopyBuffer(rw, cc, 32*1024)
}()
for {
err := func() error {
req, err := http.ReadRequest(bufio.NewReader(rw))
if err != nil {
return err
}
if log.IsLevelEnabled(logger.TraceLevel) {
dump, _ := httputil.DumpRequest(req, false)
log.Trace(string(dump))
}
req.Header.Del("Proxy-Connection")
if err = req.Write(cc); err != nil {
return err
}
return nil
}()
ch <- err
if err != nil {
break
}
}
return <-ch
}
func (h *httpHandler) decodeServerName(s string) (string, error) {
b, err := base64.RawURLEncoding.DecodeString(s)
if err != nil {
@ -256,7 +329,7 @@ func (h *httpHandler) decodeServerName(s string) (string, error) {
return string(v), nil
}
func (h *httpHandler) basicProxyAuth(proxyAuth string, log logger.Logger) (username, password string, ok bool) {
func (h *httpHandler) basicProxyAuth(proxyAuth string) (username, password string, ok bool) {
if proxyAuth == "" {
return
}
@ -278,7 +351,7 @@ func (h *httpHandler) basicProxyAuth(proxyAuth string, log logger.Logger) (usern
}
func (h *httpHandler) authenticate(ctx context.Context, conn net.Conn, req *http.Request, resp *http.Response, log logger.Logger) (id string, ok bool) {
u, p, _ := h.basicProxyAuth(req.Header.Get("Proxy-Authorization"), log)
u, p, _ := h.basicProxyAuth(req.Header.Get("Proxy-Authorization"))
if h.options.Auther == nil {
return "", true
}
@ -378,3 +451,25 @@ func (h *httpHandler) checkRateLimit(addr net.Addr) bool {
return true
}
func (h *httpHandler) observeStats(ctx context.Context) {
if h.options.Observer == nil {
return
}
d := h.md.observePeriod
if d < time.Millisecond {
d = 5 * time.Second
}
ticker := time.NewTicker(d)
defer ticker.Stop()
for {
select {
case <-ticker.C:
h.options.Observer.Observe(ctx, h.stats.Events())
case <-ctx.Done():
return
}
}
}

View File

@ -3,13 +3,15 @@ package http
import (
"net/http"
"strings"
"time"
mdata "github.com/go-gost/core/metadata"
mdutil "github.com/go-gost/core/metadata/util"
)
const (
defaultRealm = "gost"
defaultRealm = "gost"
defaultProxyAgent = "gost/3.0"
)
type metadata struct {
@ -18,20 +20,12 @@ type metadata struct {
header http.Header
hash string
authBasicRealm string
observePeriod time.Duration
proxyAgent string
}
func (h *httpHandler) parseMetadata(md mdata.Metadata) error {
const (
header = "header"
probeResistKey = "probeResistance"
probeResistKeyX = "probe_resist"
knock = "knock"
enableUDP = "udp"
hash = "hash"
authBasicRealm = "authBasicRealm"
)
if m := mdutil.GetStringMapString(md, header); len(m) > 0 {
if m := mdutil.GetStringMapString(md, "http.header", "header"); len(m) > 0 {
hd := http.Header{}
for k, v := range m {
hd.Add(k, v)
@ -39,22 +33,25 @@ func (h *httpHandler) parseMetadata(md mdata.Metadata) error {
h.md.header = hd
}
pr := mdutil.GetString(md, probeResistKey)
if pr == "" {
pr = mdutil.GetString(md, probeResistKeyX)
}
if pr != "" {
if pr := mdutil.GetString(md, "probeResist", "probe_resist"); 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.enableUDP = mdutil.GetBool(md, enableUDP)
h.md.hash = mdutil.GetString(md, hash)
h.md.authBasicRealm = mdutil.GetString(md, authBasicRealm)
h.md.enableUDP = mdutil.GetBool(md, "udp")
h.md.hash = mdutil.GetString(md, "hash")
h.md.authBasicRealm = mdutil.GetString(md, "authBasicRealm")
h.md.observePeriod = mdutil.GetDuration(md, "observePeriod")
h.md.proxyAgent = mdutil.GetString(md, "http.proxyAgent", "proxyAgent")
if h.md.proxyAgent == "" {
h.md.proxyAgent = defaultProxyAgent
}
return nil
}

View File

@ -23,10 +23,13 @@ import (
"github.com/go-gost/core/limiter/traffic"
"github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata"
ctxvalue "github.com/go-gost/x/internal/ctx"
"github.com/go-gost/core/observer/stats"
ctxvalue "github.com/go-gost/x/ctx"
xio "github.com/go-gost/x/internal/io"
netpkg "github.com/go-gost/x/internal/net"
stats_util "github.com/go-gost/x/internal/util/stats"
"github.com/go-gost/x/limiter/traffic/wrapper"
stats_wrapper "github.com/go-gost/x/observer/stats/wrapper"
"github.com/go-gost/x/registry"
)
@ -38,6 +41,8 @@ type http2Handler struct {
router *chain.Router
md metadata
options handler.Options
stats *stats_util.HandlerStats
cancel context.CancelFunc
}
func NewHandler(opts ...handler.Option) handler.Handler {
@ -48,6 +53,7 @@ func NewHandler(opts ...handler.Option) handler.Handler {
return &http2Handler{
options: options,
stats: stats_util.NewHandlerStats(options.Service),
}
}
@ -61,6 +67,12 @@ func (h *http2Handler) Init(md md.Metadata) error {
h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger))
}
ctx, cancel := context.WithCancel(context.Background())
h.cancel = cancel
if h.options.Observer != nil {
go h.observeStats(ctx)
}
return nil
}
@ -98,6 +110,13 @@ func (h *http2Handler) Handle(ctx context.Context, conn net.Conn, opts ...handle
)
}
func (h *http2Handler) Close() error {
if h.cancel != nil {
h.cancel()
}
return nil
}
// NOTE: there is an issue (golang/go#43989) will cause the client hangs
// when server returns an non-200 status code,
// May be fixed in go1.18.
@ -204,12 +223,20 @@ func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
return nil
}
rw := wrapper.WrapReadWriter(h.options.Limiter, xio.NewReadWriter(req.Body, flushWriter{w}), req.RemoteAddr,
rw := wrapper.WrapReadWriter(h.options.Limiter, xio.NewReadWriter(req.Body, flushWriter{w}),
traffic.NetworkOption("tcp"),
traffic.AddrOption(addr),
traffic.ClientOption(clientID),
traffic.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, addr)
netpkg.Transport(rw, cc)
@ -386,3 +413,25 @@ func (h *http2Handler) checkRateLimit(addr net.Addr) bool {
return true
}
func (h *http2Handler) observeStats(ctx context.Context) {
if h.options.Observer == nil {
return
}
d := h.md.observePeriod
if d < time.Millisecond {
d = 5 * time.Second
}
ticker := time.NewTicker(d)
defer ticker.Stop()
for {
select {
case <-ticker.C:
h.options.Observer.Observe(ctx, h.stats.Events())
case <-ctx.Done():
return
}
}
}

View File

@ -3,6 +3,7 @@ package http2
import (
"net/http"
"strings"
"time"
mdata "github.com/go-gost/core/metadata"
mdutil "github.com/go-gost/core/metadata/util"
@ -17,19 +18,11 @@ type metadata struct {
header http.Header
hash string
authBasicRealm string
observePeriod time.Duration
}
func (h *http2Handler) parseMetadata(md mdata.Metadata) error {
const (
header = "header"
probeResistKey = "probeResistance"
probeResistKeyX = "probe_resist"
knock = "knock"
hash = "hash"
authBasicRealm = "authBasicRealm"
)
if m := mdutil.GetStringMapString(md, header); len(m) > 0 {
if m := mdutil.GetStringMapString(md, "http.header", "header"); len(m) > 0 {
hd := http.Header{}
for k, v := range m {
hd.Add(k, v)
@ -37,21 +30,19 @@ func (h *http2Handler) parseMetadata(md mdata.Metadata) error {
h.md.header = hd
}
pr := mdutil.GetString(md, probeResistKey)
if pr == "" {
pr = mdutil.GetString(md, probeResistKeyX)
}
if pr != "" {
if pr := mdutil.GetString(md, "probeResist", "probe_resist"); 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.authBasicRealm = mdutil.GetString(md, authBasicRealm)
h.md.hash = mdutil.GetString(md, "hash")
h.md.authBasicRealm = mdutil.GetString(md, "authBasicRealm")
h.md.observePeriod = mdutil.GetDuration(md, "observePeriod")
return nil
}

View File

@ -14,7 +14,7 @@ import (
"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/internal/ctx"
ctxvalue "github.com/go-gost/x/ctx"
"github.com/go-gost/x/registry"
)

View File

@ -109,9 +109,10 @@ func (h *redirectHandler) Handle(ctx context.Context, conn net.Conn, opts ...han
conn.SetReadDeadline(time.Time{})
}
rw = xio.NewReadWriter(io.MultiReader(bytes.NewReader(hdr[:n]), rw), rw)
tlsVersion := binary.BigEndian.Uint16(hdr[1:3])
if err == nil &&
hdr[0] == dissector.Handshake &&
binary.BigEndian.Uint16(hdr[1:3]) == tls.VersionTLS10 {
(tlsVersion >= tls.VersionTLS10 && tlsVersion <= tls.VersionTLS13) {
return h.handleHTTPS(ctx, rw, conn.RemoteAddr(), dstAddr, log)
}

View File

@ -0,0 +1,64 @@
package redirect
import (
"fmt"
"net"
"os/exec"
"strconv"
"strings"
)
func (h *redirectHandler) getOriginalDstAddr(conn net.Conn) (addr net.Addr, err error) {
host, port, err := localToRemote(conn)
if err != nil {
return nil, err
}
portNumber, _ := strconv.Atoi(port)
addr = &net.TCPAddr{
IP: net.ParseIP(host),
Port: portNumber,
}
return
}
func localToRemote(clientConn net.Conn) (string, string, error) {
host, port, err := net.SplitHostPort(clientConn.RemoteAddr().String())
if err != nil {
return "", "", err
}
out, err := exec.Command("sudo", "-n", "/sbin/pfctl", "-s", "state").Output()
if err != nil {
return "", "", err
}
remoteAddr, remotePort, err := translatePfctlOutput(host, port, string(out))
if err != nil {
return "", "", err
}
return remoteAddr, remotePort, err
}
func translatePfctlOutput(address string, port, s string) (string, string, error) {
// We may get an ipv4-mapped ipv6 address here, e.g. ::ffff:127.0.0.1.
// Those still appear as "127.0.0.1" in the table, so we need to strip the prefix.
// re := regexp.MustCompile(`^::ffff:((\d+\.\d+\.\d+\.\d+$))`)
// strippedAddress := re.ReplaceAllString(address, "")
strippedAddress := address
// ALL tcp 192.168.1.13:57474 -> 23.205.82.58:443 ESTABLISHED:ESTABLISHED
spec := net.JoinHostPort(strippedAddress, port)
lines := strings.Split(s, "\n")
for _, line := range lines {
if strings.Contains(line, "ESTABLISHED:ESTABLISHED") {
if strings.Contains(line, spec) {
fields := strings.Fields(line)
if len(fields) > 4 {
return net.SplitHostPort(fields[4])
}
}
}
}
return "", "", fmt.Errorf("could not resolve original destination")
}

View File

@ -1,4 +1,4 @@
//go:build !linux
//go:build !linux && !darwin
package redirect
@ -7,7 +7,7 @@ import (
"net"
)
func (h *redirectHandler) getOriginalDstAddr(conn net.Conn) (addr net.Addr, err error) {
func (h *redirectHandler) getOriginalDstAddr(_ net.Conn) (addr net.Addr, err error) {
err = errors.New("TCP redirect is not available on non-linux platform")
return
}

View File

@ -10,6 +10,7 @@ import (
"github.com/go-gost/core/listener"
"github.com/go-gost/core/logger"
"github.com/go-gost/relay"
xnet "github.com/go-gost/x/internal/net"
"github.com/go-gost/x/internal/net/udp"
"github.com/go-gost/x/internal/util/mux"
relay_util "github.com/go-gost/x/internal/util/relay"
@ -50,7 +51,10 @@ func (h *relayHandler) bindTCP(ctx context.Context, conn net.Conn, network, addr
Status: relay.StatusOK,
}
ln, err := net.Listen(network, address) // strict mode: if the port already in use, it will return error
lc := xnet.ListenConfig{
Netns: h.options.Netns,
}
ln, err := lc.Listen(ctx, network, address) // strict mode: if the port already in use, it will return error
if err != nil {
log.Error(err)
resp.Status = relay.StatusServiceUnavailable
@ -129,10 +133,10 @@ func (h *relayHandler) bindUDP(ctx context.Context, conn net.Conn, network, addr
Status: relay.StatusOK,
}
var pc net.PacketConn
var err error
bindAddr, _ := net.ResolveUDPAddr(network, address)
pc, err = net.ListenUDP(network, bindAddr)
lc := xnet.ListenConfig{
Netns: h.options.Netns,
}
pc, err := lc.ListenPacket(ctx, network, address)
if err != nil {
log.Error(err)
return err

View File

@ -10,11 +10,13 @@ import (
"github.com/go-gost/core/limiter/traffic"
"github.com/go-gost/core/logger"
"github.com/go-gost/core/observer/stats"
"github.com/go-gost/relay"
ctxvalue "github.com/go-gost/x/internal/ctx"
ctxvalue "github.com/go-gost/x/ctx"
xnet "github.com/go-gost/x/internal/net"
serial "github.com/go-gost/x/internal/util/serial"
"github.com/go-gost/x/limiter/traffic/wrapper"
stats_wrapper "github.com/go-gost/x/observer/stats/wrapper"
)
func (h *relayHandler) handleConnect(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) (err error) {
@ -105,12 +107,20 @@ func (h *relayHandler) handleConnect(ctx context.Context, conn net.Conn, network
}
}
rw := wrapper.WrapReadWriter(h.options.Limiter, conn, conn.RemoteAddr().String(),
clientID := ctxvalue.ClientIDFromContext(ctx)
rw := wrapper.WrapReadWriter(h.options.Limiter, conn,
traffic.NetworkOption(network),
traffic.AddrOption(address),
traffic.ClientOption(string(ctxvalue.ClientIDFromContext(ctx))),
traffic.ClientOption(string(clientID)),
traffic.SrcOption(conn.RemoteAddr().String()),
)
if h.options.Observer != nil {
pstats := h.stats.Stats(string(clientID))
pstats.Add(stats.KindTotalConns, 1)
pstats.Add(stats.KindCurrentConns, 1)
defer pstats.Add(stats.KindCurrentConns, -1)
rw = stats_wrapper.WrapReadWriter(rw, pstats)
}
t := time.Now()
log.Infof("%s <-> %s", conn.RemoteAddr(), address)

View File

@ -9,10 +9,12 @@ import (
"github.com/go-gost/core/limiter/traffic"
"github.com/go-gost/core/logger"
"github.com/go-gost/core/observer/stats"
"github.com/go-gost/relay"
ctxvalue "github.com/go-gost/x/internal/ctx"
ctxvalue "github.com/go-gost/x/ctx"
netpkg "github.com/go-gost/x/internal/net"
"github.com/go-gost/x/limiter/traffic/wrapper"
stats_wrapper "github.com/go-gost/x/observer/stats/wrapper"
)
func (h *relayHandler) handleForward(ctx context.Context, conn net.Conn, network string, log logger.Logger) error {
@ -87,12 +89,20 @@ func (h *relayHandler) handleForward(ctx context.Context, conn net.Conn, network
conn = rc
}
rw := wrapper.WrapReadWriter(h.options.Limiter, conn, conn.RemoteAddr().String(),
clientID := ctxvalue.ClientIDFromContext(ctx)
rw := wrapper.WrapReadWriter(h.options.Limiter, conn,
traffic.NetworkOption(network),
traffic.AddrOption(target.Addr),
traffic.ClientOption(string(ctxvalue.ClientIDFromContext(ctx))),
traffic.ClientOption(string(clientID)),
traffic.SrcOption(conn.RemoteAddr().String()),
)
if h.options.Observer != nil {
pstats := h.stats.Stats(string(clientID))
pstats.Add(stats.KindTotalConns, 1)
pstats.Add(stats.KindCurrentConns, 1)
defer pstats.Add(stats.KindCurrentConns, -1)
rw = stats_wrapper.WrapReadWriter(rw, pstats)
}
t := time.Now()
log.Debugf("%s <-> %s", conn.RemoteAddr(), target.Addr)

View File

@ -11,9 +11,9 @@ import (
"github.com/go-gost/core/handler"
"github.com/go-gost/core/hop"
md "github.com/go-gost/core/metadata"
"github.com/go-gost/core/service"
"github.com/go-gost/relay"
ctxvalue "github.com/go-gost/x/internal/ctx"
ctxvalue "github.com/go-gost/x/ctx"
stats_util "github.com/go-gost/x/internal/util/stats"
"github.com/go-gost/x/registry"
)
@ -33,7 +33,8 @@ type relayHandler struct {
router *chain.Router
md metadata
options handler.Options
ep service.Service
stats *stats_util.HandlerStats
cancel context.CancelFunc
}
func NewHandler(opts ...handler.Option) handler.Handler {
@ -44,6 +45,7 @@ func NewHandler(opts ...handler.Option) handler.Handler {
return &relayHandler{
options: options,
stats: stats_util.NewHandlerStats(options.Service),
}
}
@ -57,6 +59,13 @@ func (h *relayHandler) Init(md md.Metadata) (err error) {
h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger))
}
ctx, cancel := context.WithCancel(context.Background())
h.cancel = cancel
if h.options.Observer != nil {
go h.observeStats(ctx)
}
return nil
}
@ -172,8 +181,8 @@ func (h *relayHandler) Handle(ctx context.Context, conn net.Conn, opts ...handle
// Close implements io.Closer interface.
func (h *relayHandler) Close() error {
if h.ep != nil {
return h.ep.Close()
if h.cancel != nil {
h.cancel()
}
return nil
}
@ -189,3 +198,25 @@ func (h *relayHandler) checkRateLimit(addr net.Addr) bool {
return true
}
func (h *relayHandler) observeStats(ctx context.Context) {
if h.options.Observer == nil {
return
}
d := h.md.observePeriod
if d < time.Millisecond {
d = 5 * time.Second
}
ticker := time.NewTicker(d)
defer ticker.Stop()
for {
select {
case <-ticker.C:
h.options.Observer.Observe(ctx, h.stats.Events())
case <-ctx.Done():
return
}
}
}

View File

@ -16,28 +16,21 @@ type metadata struct {
noDelay bool
hash string
muxCfg *mux.Config
observePeriod time.Duration
}
func (h *relayHandler) parseMetadata(md mdata.Metadata) (err error) {
const (
readTimeout = "readTimeout"
enableBind = "bind"
udpBufferSize = "udpBufferSize"
noDelay = "nodelay"
hash = "hash"
)
h.md.readTimeout = mdutil.GetDuration(md, "readTimeout")
h.md.enableBind = mdutil.GetBool(md, "bind")
h.md.noDelay = mdutil.GetBool(md, "nodelay")
h.md.readTimeout = mdutil.GetDuration(md, readTimeout)
h.md.enableBind = mdutil.GetBool(md, enableBind)
h.md.noDelay = mdutil.GetBool(md, noDelay)
if bs := mdutil.GetInt(md, udpBufferSize); bs > 0 {
if bs := mdutil.GetInt(md, "udpBufferSize"); bs > 0 {
h.md.udpBufferSize = int(math.Min(math.Max(float64(bs), 512), 64*1024))
} else {
h.md.udpBufferSize = 4096
}
h.md.hash = mdutil.GetString(md, hash)
h.md.hash = mdutil.GetString(md, "hash")
h.md.muxCfg = &mux.Config{
Version: mdutil.GetInt(md, "mux.version"),
@ -49,5 +42,7 @@ func (h *relayHandler) parseMetadata(md mdata.Metadata) (err error) {
MaxStreamBuffer: mdutil.GetInt(md, "mux.maxStreamBuffer"),
}
h.md.observePeriod = mdutil.GetDuration(md, "observePeriod")
return
}

View File

@ -21,7 +21,7 @@ import (
"github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata"
dissector "github.com/go-gost/tls-dissector"
ctxvalue "github.com/go-gost/x/internal/ctx"
ctxvalue "github.com/go-gost/x/ctx"
xio "github.com/go-gost/x/internal/io"
netpkg "github.com/go-gost/x/internal/net"
"github.com/go-gost/x/registry"
@ -90,8 +90,10 @@ func (h *sniHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.
}
rw := xio.NewReadWriter(io.MultiReader(bytes.NewReader(hdr[:]), conn), conn)
tlsVersion := binary.BigEndian.Uint16(hdr[1:3])
if hdr[0] == dissector.Handshake &&
binary.BigEndian.Uint16(hdr[1:3]) == tls.VersionTLS10 {
(tlsVersion >= tls.VersionTLS10 && tlsVersion <= tls.VersionTLS13) {
return h.handleHTTPS(ctx, rw, conn.RemoteAddr(), log)
}
return h.handleHTTP(ctx, rw, conn.RemoteAddr(), log)

View File

@ -11,10 +11,13 @@ import (
"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/stats"
"github.com/go-gost/gosocks4"
ctxvalue "github.com/go-gost/x/internal/ctx"
ctxvalue "github.com/go-gost/x/ctx"
netpkg "github.com/go-gost/x/internal/net"
stats_util "github.com/go-gost/x/internal/util/stats"
"github.com/go-gost/x/limiter/traffic/wrapper"
stats_wrapper "github.com/go-gost/x/observer/stats/wrapper"
"github.com/go-gost/x/registry"
)
@ -32,6 +35,8 @@ type socks4Handler struct {
router *chain.Router
md metadata
options handler.Options
stats *stats_util.HandlerStats
cancel context.CancelFunc
}
func NewHandler(opts ...handler.Option) handler.Handler {
@ -42,6 +47,7 @@ func NewHandler(opts ...handler.Option) handler.Handler {
return &socks4Handler{
options: options,
stats: stats_util.NewHandlerStats(options.Service),
}
}
@ -55,6 +61,13 @@ func (h *socks4Handler) Init(md md.Metadata) (err error) {
h.router = chain.NewRouter(chain.LoggerRouterOption(h.options.Logger))
}
ctx, cancel := context.WithCancel(context.Background())
h.cancel = cancel
if h.options.Observer != nil {
go h.observeStats(ctx)
}
return nil
}
@ -114,6 +127,13 @@ func (h *socks4Handler) Handle(ctx context.Context, conn net.Conn, opts ...handl
}
}
func (h *socks4Handler) Close() error {
if h.cancel != nil {
h.cancel()
}
return nil
}
func (h *socks4Handler) handleConnect(ctx context.Context, conn net.Conn, req *gosocks4.Request, log logger.Logger) error {
addr := req.Addr.String()
@ -151,12 +171,20 @@ func (h *socks4Handler) handleConnect(ctx context.Context, conn net.Conn, req *g
return err
}
rw := wrapper.WrapReadWriter(h.options.Limiter, conn, conn.RemoteAddr().String(),
clientID := ctxvalue.ClientIDFromContext(ctx)
rw := wrapper.WrapReadWriter(h.options.Limiter, conn,
traffic.NetworkOption("tcp"),
traffic.AddrOption(addr),
traffic.ClientOption(string(ctxvalue.ClientIDFromContext(ctx))),
traffic.ClientOption(string(clientID)),
traffic.SrcOption(conn.RemoteAddr().String()),
)
if h.options.Observer != nil {
pstats := h.stats.Stats(string(clientID))
pstats.Add(stats.KindTotalConns, 1)
pstats.Add(stats.KindCurrentConns, 1)
defer pstats.Add(stats.KindCurrentConns, -1)
rw = stats_wrapper.WrapReadWriter(rw, pstats)
}
t := time.Now()
log.Infof("%s <-> %s", conn.RemoteAddr(), addr)
@ -184,3 +212,25 @@ func (h *socks4Handler) checkRateLimit(addr net.Addr) bool {
return true
}
func (h *socks4Handler) observeStats(ctx context.Context) {
if h.options.Observer == nil {
return
}
d := h.md.observePeriod
if d < time.Millisecond {
d = 5 * time.Second
}
ticker := time.NewTicker(d)
defer ticker.Stop()
for {
select {
case <-ticker.C:
h.options.Observer.Observe(ctx, h.stats.Events())
case <-ctx.Done():
return
}
}
}

View File

@ -8,17 +8,14 @@ import (
)
type metadata struct {
readTimeout time.Duration
hash string
readTimeout time.Duration
hash string
observePeriod time.Duration
}
func (h *socks4Handler) parseMetadata(md mdata.Metadata) (err error) {
const (
readTimeout = "readTimeout"
hash = "hash"
)
h.md.readTimeout = mdutil.GetDuration(md, readTimeout)
h.md.hash = mdutil.GetString(md, hash)
h.md.readTimeout = mdutil.GetDuration(md, "readTimeout")
h.md.hash = mdutil.GetString(md, "hash")
h.md.observePeriod = mdutil.GetDuration(md, "observePeriod")
return
}

View File

@ -9,6 +9,7 @@ import (
"github.com/go-gost/core/logger"
"github.com/go-gost/gosocks5"
netpkg "github.com/go-gost/x/internal/net"
xnet "github.com/go-gost/x/internal/net"
)
func (h *socks5Handler) handleBind(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) error {
@ -31,7 +32,10 @@ func (h *socks5Handler) handleBind(ctx context.Context, conn net.Conn, network,
}
func (h *socks5Handler) bindLocal(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) error {
ln, err := net.Listen(network, address) // strict mode: if the port already in use, it will return error
lc := xnet.ListenConfig{
Netns: h.options.Netns,
}
ln, err := lc.Listen(ctx, network, address) // strict mode: if the port already in use, it will return error
if err != nil {
log.Error(err)
reply := gosocks5.NewReply(gosocks5.Failure, nil)
@ -95,7 +99,7 @@ func (h *socks5Handler) serveBind(ctx context.Context, conn net.Conn, ln net.Lis
defer close(errc)
defer pc1.Close()
errc <- netpkg.Transport(conn, pc1)
errc <- xnet.Transport(conn, pc1)
}()
return errc

View File

@ -8,10 +8,12 @@ import (
"github.com/go-gost/core/limiter/traffic"
"github.com/go-gost/core/logger"
"github.com/go-gost/core/observer/stats"
"github.com/go-gost/gosocks5"
ctxvalue "github.com/go-gost/x/internal/ctx"
ctxvalue "github.com/go-gost/x/ctx"
netpkg "github.com/go-gost/x/internal/net"
"github.com/go-gost/x/limiter/traffic/wrapper"
stats_wrapper "github.com/go-gost/x/observer/stats/wrapper"
)
func (h *socks5Handler) handleConnect(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) error {
@ -50,12 +52,20 @@ func (h *socks5Handler) handleConnect(ctx context.Context, conn net.Conn, networ
return err
}
rw := wrapper.WrapReadWriter(h.options.Limiter, conn, conn.RemoteAddr().String(),
clientID := ctxvalue.ClientIDFromContext(ctx)
rw := wrapper.WrapReadWriter(h.options.Limiter, conn,
traffic.NetworkOption(network),
traffic.AddrOption(address),
traffic.ClientOption(string(ctxvalue.ClientIDFromContext(ctx))),
traffic.ClientOption(string(clientID)),
traffic.SrcOption(conn.RemoteAddr().String()),
)
if h.options.Observer != nil {
pstats := h.stats.Stats(string(clientID))
pstats.Add(stats.KindTotalConns, 1)
pstats.Add(stats.KindCurrentConns, 1)
defer pstats.Add(stats.KindCurrentConns, -1)
rw = stats_wrapper.WrapReadWriter(rw, pstats)
}
t := time.Now()
log.Infof("%s <-> %s", conn.RemoteAddr(), address)

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