Merge branch 'master' into dev

# Conflicts:
#	go.mod
#	go.sum
This commit is contained in:
wenyifan 2024-06-19 10:10:55 +08:00
commit 423dd1e35d
79 changed files with 2085 additions and 631 deletions

View File

@ -165,6 +165,18 @@ func registerConfig(config *gin.RouterGroup) {
config.PUT("/routers/:router", updateRouter) config.PUT("/routers/:router", updateRouter)
config.DELETE("/routers/:router", deleteRouter) 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.POST("/limiters", createLimiter)
config.PUT("/limiters/:limiter", updateLimiter) config.PUT("/limiters/:limiter", updateLimiter)
config.DELETE("/limiters/:limiter", deleteLimiter) config.DELETE("/limiters/:limiter", deleteLimiter)

View File

@ -144,7 +144,7 @@ func saveConfig(ctx *gin.Context) {
if err != nil { if err != nil {
writeError(ctx, &Error{ writeError(ctx, &Error{
statusCode: http.StatusInternalServerError, statusCode: http.StatusInternalServerError,
Code: 40005, Code: ErrCodeSaveConfigFailed,
Msg: fmt.Sprintf("create file: %s", err.Error()), Msg: fmt.Sprintf("create file: %s", err.Error()),
}) })
return return
@ -154,8 +154,8 @@ func saveConfig(ctx *gin.Context) {
if err := config.Global().Write(f, req.Format); err != nil { if err := config.Global().Write(f, req.Format); err != nil {
writeError(ctx, &Error{ writeError(ctx, &Error{
statusCode: http.StatusInternalServerError, statusCode: http.StatusInternalServerError,
Code: 40006, Code: ErrCodeSaveConfigFailed,
Msg: fmt.Sprintf("write: %s", err.Error()), Msg: fmt.Sprintf("save config: %s", err.Error()),
}) })
return return
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,9 @@
package api package api
import ( import (
"fmt"
"net/http" "net/http"
"strings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-gost/x/config" "github.com/go-gost/x/config"
@ -35,15 +37,22 @@ func createLimiter(ctx *gin.Context) {
var req createLimiterRequest var req createLimiterRequest
ctx.ShouldBindJSON(&req.Data) ctx.ShouldBindJSON(&req.Data)
if req.Data.Name == "" { name := strings.TrimSpace(req.Data.Name)
writeError(ctx, ErrInvalid) 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 return
} }
v := parser.ParseTrafficLimiter(&req.Data) v := parser.ParseTrafficLimiter(&req.Data)
if err := registry.TrafficLimiterRegistry().Register(req.Data.Name, v); err != nil { if err := registry.TrafficLimiterRegistry().Register(name, v); err != nil {
writeError(ctx, ErrDup) writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name)))
return return
} }
@ -87,25 +96,27 @@ func updateLimiter(ctx *gin.Context) {
ctx.ShouldBindUri(&req) ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data) ctx.ShouldBindJSON(&req.Data)
if !registry.TrafficLimiterRegistry().IsRegistered(req.Limiter) { name := strings.TrimSpace(req.Limiter)
writeError(ctx, ErrNotFound)
if !registry.TrafficLimiterRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("limiter %s not found", name)))
return return
} }
req.Data.Name = req.Limiter req.Data.Name = name
v := parser.ParseTrafficLimiter(&req.Data) v := parser.ParseTrafficLimiter(&req.Data)
registry.TrafficLimiterRegistry().Unregister(req.Limiter) registry.TrafficLimiterRegistry().Unregister(name)
if err := registry.TrafficLimiterRegistry().Register(req.Limiter, v); err != nil { if err := registry.TrafficLimiterRegistry().Register(name, v); err != nil {
writeError(ctx, ErrDup) writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name)))
return return
} }
config.OnUpdate(func(c *config.Config) error { config.OnUpdate(func(c *config.Config) error {
for i := range c.Limiters { for i := range c.Limiters {
if c.Limiters[i].Name == req.Limiter { if c.Limiters[i].Name == name {
c.Limiters[i] = &req.Data c.Limiters[i] = &req.Data
break break
} }
@ -145,17 +156,19 @@ func deleteLimiter(ctx *gin.Context) {
var req deleteLimiterRequest var req deleteLimiterRequest
ctx.ShouldBindUri(&req) ctx.ShouldBindUri(&req)
if !registry.TrafficLimiterRegistry().IsRegistered(req.Limiter) { name := strings.TrimSpace(req.Limiter)
writeError(ctx, ErrNotFound)
if !registry.TrafficLimiterRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("limiter %s not found", name)))
return return
} }
registry.TrafficLimiterRegistry().Unregister(req.Limiter) registry.TrafficLimiterRegistry().Unregister(name)
config.OnUpdate(func(c *config.Config) error { config.OnUpdate(func(c *config.Config) error {
limiteres := c.Limiters limiteres := c.Limiters
c.Limiters = nil c.Limiters = nil
for _, s := range limiteres { for _, s := range limiteres {
if s.Name == req.Limiter { if s.Name == name {
continue continue
} }
c.Limiters = append(c.Limiters, s) 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 package api
import ( import (
"fmt"
"net/http" "net/http"
"strings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-gost/x/config" "github.com/go-gost/x/config"
@ -35,15 +37,22 @@ func createRateLimiter(ctx *gin.Context) {
var req createRateLimiterRequest var req createRateLimiterRequest
ctx.ShouldBindJSON(&req.Data) ctx.ShouldBindJSON(&req.Data)
if req.Data.Name == "" { name := strings.TrimSpace(req.Data.Name)
writeError(ctx, ErrInvalid) 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 return
} }
v := parser.ParseRateLimiter(&req.Data) v := parser.ParseRateLimiter(&req.Data)
if err := registry.RateLimiterRegistry().Register(req.Data.Name, v); err != nil { if err := registry.RateLimiterRegistry().Register(name, v); err != nil {
writeError(ctx, ErrDup) writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name)))
return return
} }
@ -87,25 +96,27 @@ func updateRateLimiter(ctx *gin.Context) {
ctx.ShouldBindUri(&req) ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data) ctx.ShouldBindJSON(&req.Data)
if !registry.RateLimiterRegistry().IsRegistered(req.Limiter) { name := strings.TrimSpace(req.Limiter)
writeError(ctx, ErrNotFound)
if !registry.RateLimiterRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("limiter %s not found", name)))
return return
} }
req.Data.Name = req.Limiter req.Data.Name = name
v := parser.ParseRateLimiter(&req.Data) v := parser.ParseRateLimiter(&req.Data)
registry.RateLimiterRegistry().Unregister(req.Limiter) registry.RateLimiterRegistry().Unregister(name)
if err := registry.RateLimiterRegistry().Register(req.Limiter, v); err != nil { if err := registry.RateLimiterRegistry().Register(name, v); err != nil {
writeError(ctx, ErrDup) writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("limiter %s already exists", name)))
return return
} }
config.OnUpdate(func(c *config.Config) error { config.OnUpdate(func(c *config.Config) error {
for i := range c.RLimiters { for i := range c.RLimiters {
if c.RLimiters[i].Name == req.Limiter { if c.RLimiters[i].Name == name {
c.RLimiters[i] = &req.Data c.RLimiters[i] = &req.Data
break break
} }
@ -145,17 +156,19 @@ func deleteRateLimiter(ctx *gin.Context) {
var req deleteRateLimiterRequest var req deleteRateLimiterRequest
ctx.ShouldBindUri(&req) ctx.ShouldBindUri(&req)
if !registry.RateLimiterRegistry().IsRegistered(req.Limiter) { name := strings.TrimSpace(req.Limiter)
writeError(ctx, ErrNotFound)
if !registry.RateLimiterRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("limiter %s not found", name)))
return return
} }
registry.RateLimiterRegistry().Unregister(req.Limiter) registry.RateLimiterRegistry().Unregister(name)
config.OnUpdate(func(c *config.Config) error { config.OnUpdate(func(c *config.Config) error {
limiteres := c.RLimiters limiteres := c.RLimiters
c.RLimiters = nil c.RLimiters = nil
for _, s := range limiteres { for _, s := range limiteres {
if s.Name == req.Limiter { if s.Name == name {
continue continue
} }
c.RLimiters = append(c.RLimiters, s) 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 package api
import ( import (
"fmt"
"net/http" "net/http"
"strings"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-gost/x/config" "github.com/go-gost/x/config"
@ -35,19 +37,26 @@ func createResolver(ctx *gin.Context) {
var req createResolverRequest var req createResolverRequest
ctx.ShouldBindJSON(&req.Data) ctx.ShouldBindJSON(&req.Data)
if req.Data.Name == "" { name := strings.TrimSpace(req.Data.Name)
writeError(ctx, ErrInvalid) 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 return
} }
v, err := parser.ParseResolver(&req.Data) v, err := parser.ParseResolver(&req.Data)
if err != nil { if err != nil {
writeError(ctx, ErrCreate) writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create resolver %s failed: %s", name, err.Error())))
return return
} }
if err := registry.ResolverRegistry().Register(req.Data.Name, v); err != nil { if err := registry.ResolverRegistry().Register(name, v); err != nil {
writeError(ctx, ErrDup) writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("resolver %s already exists", name)))
return return
} }
@ -91,29 +100,31 @@ func updateResolver(ctx *gin.Context) {
ctx.ShouldBindUri(&req) ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data) ctx.ShouldBindJSON(&req.Data)
if !registry.ResolverRegistry().IsRegistered(req.Resolver) { name := strings.TrimSpace(req.Resolver)
writeError(ctx, ErrNotFound)
if !registry.ResolverRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("resolver %s not found", name)))
return return
} }
req.Data.Name = req.Resolver req.Data.Name = name
v, err := parser.ParseResolver(&req.Data) v, err := parser.ParseResolver(&req.Data)
if err != nil { if err != nil {
writeError(ctx, ErrCreate) writeError(ctx, NewError(http.StatusInternalServerError, ErrCodeFailed, fmt.Sprintf("create resolver %s failed: %s", name, err.Error())))
return return
} }
registry.ResolverRegistry().Unregister(req.Resolver) registry.ResolverRegistry().Unregister(name)
if err := registry.ResolverRegistry().Register(req.Resolver, v); err != nil { if err := registry.ResolverRegistry().Register(name, v); err != nil {
writeError(ctx, ErrDup) writeError(ctx, NewError(http.StatusBadRequest, ErrCodeDup, fmt.Sprintf("resolver %s already exists", name)))
return return
} }
config.OnUpdate(func(c *config.Config) error { config.OnUpdate(func(c *config.Config) error {
for i := range c.Resolvers { for i := range c.Resolvers {
if c.Resolvers[i].Name == req.Resolver { if c.Resolvers[i].Name == name {
c.Resolvers[i] = &req.Data c.Resolvers[i] = &req.Data
break break
} }
@ -153,17 +164,19 @@ func deleteResolver(ctx *gin.Context) {
var req deleteResolverRequest var req deleteResolverRequest
ctx.ShouldBindUri(&req) ctx.ShouldBindUri(&req)
if !registry.ResolverRegistry().IsRegistered(req.Resolver) { name := strings.TrimSpace(req.Resolver)
writeError(ctx, ErrNotFound)
if !registry.ResolverRegistry().IsRegistered(name) {
writeError(ctx, NewError(http.StatusBadRequest, ErrCodeNotFound, fmt.Sprintf("resolver %s not found", name)))
return return
} }
registry.ResolverRegistry().Unregister(req.Resolver) registry.ResolverRegistry().Unregister(name)
config.OnUpdate(func(c *config.Config) error { config.OnUpdate(func(c *config.Config) error {
resolvers := c.Resolvers resolvers := c.Resolvers
c.Resolvers = nil c.Resolvers = nil
for _, s := range resolvers { for _, s := range resolvers {
if s.Name == req.Resolver { if s.Name == name {
continue continue
} }
c.Resolvers = append(c.Resolvers, s) c.Resolvers = append(c.Resolvers, s)

View File

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

View File

@ -15,6 +15,16 @@ var (
ErrSave = &Error{statusCode: http.StatusInternalServerError, Code: 40005, Msg: "save config failed"} 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. // Error is an api error.
type Error struct { type Error struct {
statusCode int statusCode int
@ -22,6 +32,14 @@ type Error struct {
Msg string `json:"msg"` 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 { func (e *Error) Error() string {
b, _ := json.Marshal(e) b, _ := json.Marshal(e)
return string(b) return string(b)

View File

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

View File

@ -188,8 +188,18 @@ definitions:
x-go-name: Limiters x-go-name: Limiters
log: log:
$ref: '#/definitions/LogConfig' $ref: '#/definitions/LogConfig'
loggers:
items:
$ref: '#/definitions/LoggerConfig'
type: array
x-go-name: Loggers
metrics: metrics:
$ref: '#/definitions/MetricsConfig' $ref: '#/definitions/MetricsConfig'
observers:
items:
$ref: '#/definitions/ObserverConfig'
type: array
x-go-name: Observers
profiling: profiling:
$ref: '#/definitions/ProfilingConfig' $ref: '#/definitions/ProfilingConfig'
recorders: recorders:
@ -296,11 +306,18 @@ definitions:
type: string type: string
type: array type: array
x-go-name: Bypasses x-go-name: Bypasses
filter:
$ref: '#/definitions/NodeFilterConfig'
host: host:
description: DEPRECATED by filter.host
type: string type: string
x-go-name: Host x-go-name: Host
http: http:
$ref: '#/definitions/HTTPNodeConfig' $ref: '#/definitions/HTTPNodeConfig'
metadata:
additionalProperties: {}
type: object
x-go-name: Metadata
name: name:
type: string type: string
x-go-name: Name x-go-name: Name
@ -308,9 +325,11 @@ definitions:
type: string type: string
x-go-name: Network x-go-name: Network
path: path:
description: DEPRECATED by filter.path
type: string type: string
x-go-name: Path x-go-name: Path
protocol: protocol:
description: DEPRECATED by filter.protocol
type: string type: string
x-go-name: Protocol x-go-name: Protocol
tls: tls:
@ -319,7 +338,12 @@ definitions:
x-go-package: github.com/go-gost/x/config x-go-package: github.com/go-gost/x/config
ForwarderConfig: ForwarderConfig:
properties: properties:
hop:
description: the referenced hop name
type: string
x-go-name: Hop
name: name:
description: DEPRECATED by hop field
type: string type: string
x-go-name: Name x-go-name: Name
nodes: nodes:
@ -342,6 +366,8 @@ definitions:
x-go-package: github.com/go-gost/x/config x-go-package: github.com/go-gost/x/config
HTTPNodeConfig: HTTPNodeConfig:
properties: properties:
auth:
$ref: '#/definitions/AuthConfig'
header: header:
additionalProperties: additionalProperties:
type: string type: string
@ -350,6 +376,11 @@ definitions:
host: host:
type: string type: string
x-go-name: Host x-go-name: Host
rewrite:
items:
$ref: '#/definitions/HTTPURLRewriteConfig'
type: array
x-go-name: Rewrite
type: object type: object
x-go-package: github.com/go-gost/x/config x-go-package: github.com/go-gost/x/config
HTTPRecorder: HTTPRecorder:
@ -361,6 +392,14 @@ definitions:
x-go-name: URL x-go-name: URL
type: object type: object
x-go-package: github.com/go-gost/x/config 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: HandlerConfig:
properties: properties:
auth: auth:
@ -385,6 +424,9 @@ definitions:
additionalProperties: {} additionalProperties: {}
type: object type: object
x-go-name: Metadata x-go-name: Metadata
observer:
type: string
x-go-name: Observer
retries: retries:
format: int64 format: int64
type: integer type: integer
@ -615,6 +657,15 @@ definitions:
x-go-name: MaxSize x-go-name: MaxSize
type: object type: object
x-go-package: github.com/go-gost/x/config 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: MetricsConfig:
properties: properties:
addr: addr:
@ -664,8 +715,6 @@ definitions:
addr: addr:
type: string type: string
x-go-name: Addr x-go-name: Addr
auth:
$ref: '#/definitions/AuthConfig'
bypass: bypass:
type: string type: string
x-go-name: Bypass x-go-name: Bypass
@ -678,9 +727,8 @@ definitions:
$ref: '#/definitions/ConnectorConfig' $ref: '#/definitions/ConnectorConfig'
dialer: dialer:
$ref: '#/definitions/DialerConfig' $ref: '#/definitions/DialerConfig'
host: filter:
type: string $ref: '#/definitions/NodeFilterConfig'
x-go-name: Host
hosts: hosts:
type: string type: string
x-go-name: Hosts x-go-name: Hosts
@ -699,12 +747,6 @@ definitions:
network: network:
type: string type: string
x-go-name: Network x-go-name: Network
path:
type: string
x-go-name: Path
protocol:
type: string
x-go-name: Protocol
resolver: resolver:
type: string type: string
x-go-name: Resolver x-go-name: Resolver
@ -714,6 +756,28 @@ definitions:
$ref: '#/definitions/TLSNodeConfig' $ref: '#/definitions/TLSNodeConfig'
type: object type: object
x-go-package: github.com/go-gost/x/config 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: PluginConfig:
properties: properties:
addr: addr:
@ -927,6 +991,14 @@ definitions:
x-go-name: Limiter x-go-name: Limiter
listener: listener:
$ref: '#/definitions/ListenerConfig' $ref: '#/definitions/ListenerConfig'
logger:
type: string
x-go-name: Logger
loggers:
items:
type: string
type: array
x-go-name: Loggers
metadata: metadata:
additionalProperties: {} additionalProperties: {}
type: object type: object
@ -934,6 +1006,9 @@ definitions:
name: name:
type: string type: string
x-go-name: Name x-go-name: Name
observer:
type: string
x-go-name: Observer
recorders: recorders:
items: items:
$ref: '#/definitions/RecorderObject' $ref: '#/definitions/RecorderObject'
@ -947,6 +1022,61 @@ definitions:
x-go-name: RLimiter x-go-name: RLimiter
sockopts: sockopts:
$ref: '#/definitions/SockOptsConfig' $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 type: object
x-go-package: github.com/go-gost/x/config x-go-package: github.com/go-gost/x/config
SockOptsConfig: SockOptsConfig:
@ -1588,6 +1718,122 @@ paths:
summary: Update limiter by name, the limiter must already exist. summary: Update limiter by name, the limiter must already exist.
tags: tags:
- Limiter - 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: /config/resolvers:
post: post:
operationId: createResolverRequest operationId: createResolverRequest
@ -1762,6 +2008,64 @@ paths:
summary: Update router by name, the router must already exist. summary: Update router by name, the router must already exist.
tags: tags:
- Router - 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: /config/services:
post: post:
operationId: createServiceRequest operationId: createServiceRequest
@ -1877,12 +2181,24 @@ responses:
Data: {} Data: {}
schema: schema:
$ref: '#/definitions/Response' $ref: '#/definitions/Response'
createObserverResponse:
description: successful operation.
headers:
Data: {}
schema:
$ref: '#/definitions/Response'
createRateLimiterResponse: createRateLimiterResponse:
description: successful operation. description: successful operation.
headers: headers:
Data: {} Data: {}
schema: schema:
$ref: '#/definitions/Response' $ref: '#/definitions/Response'
createRecorderResponse:
description: successful operation.
headers:
Data: {}
schema:
$ref: '#/definitions/Response'
createResolverResponse: createResolverResponse:
description: successful operation. description: successful operation.
headers: headers:
@ -1895,6 +2211,12 @@ responses:
Data: {} Data: {}
schema: schema:
$ref: '#/definitions/Response' $ref: '#/definitions/Response'
createSDResponse:
description: successful operation.
headers:
Data: {}
schema:
$ref: '#/definitions/Response'
createServiceResponse: createServiceResponse:
description: successful operation. description: successful operation.
headers: headers:
@ -1955,12 +2277,24 @@ responses:
Data: {} Data: {}
schema: schema:
$ref: '#/definitions/Response' $ref: '#/definitions/Response'
deleteObserverResponse:
description: successful operation.
headers:
Data: {}
schema:
$ref: '#/definitions/Response'
deleteRateLimiterResponse: deleteRateLimiterResponse:
description: successful operation. description: successful operation.
headers: headers:
Data: {} Data: {}
schema: schema:
$ref: '#/definitions/Response' $ref: '#/definitions/Response'
deleteRecorderResponse:
description: successful operation.
headers:
Data: {}
schema:
$ref: '#/definitions/Response'
deleteResolverResponse: deleteResolverResponse:
description: successful operation. description: successful operation.
headers: headers:
@ -1973,6 +2307,12 @@ responses:
Data: {} Data: {}
schema: schema:
$ref: '#/definitions/Response' $ref: '#/definitions/Response'
deleteSDResponse:
description: successful operation.
headers:
Data: {}
schema:
$ref: '#/definitions/Response'
deleteServiceResponse: deleteServiceResponse:
description: successful operation. description: successful operation.
headers: headers:
@ -2045,12 +2385,24 @@ responses:
Data: {} Data: {}
schema: schema:
$ref: '#/definitions/Response' $ref: '#/definitions/Response'
updateObserverResponse:
description: successful operation.
headers:
Data: {}
schema:
$ref: '#/definitions/Response'
updateRateLimiterResponse: updateRateLimiterResponse:
description: successful operation. description: successful operation.
headers: headers:
Data: {} Data: {}
schema: schema:
$ref: '#/definitions/Response' $ref: '#/definitions/Response'
updateRecorderResponse:
description: successful operation.
headers:
Data: {}
schema:
$ref: '#/definitions/Response'
updateResolverResponse: updateResolverResponse:
description: successful operation. description: successful operation.
headers: headers:
@ -2063,6 +2415,12 @@ responses:
Data: {} Data: {}
schema: schema:
$ref: '#/definitions/Response' $ref: '#/definitions/Response'
updateSDResponse:
description: successful operation.
headers:
Data: {}
schema:
$ref: '#/definitions/Response'
updateServiceResponse: updateServiceResponse:
description: successful operation. description: successful operation.
headers: headers:

View File

@ -235,11 +235,17 @@ func (bp *localBypass) Contains(ctx context.Context, network, addr string, opts
b := !bp.options.whitelist && matched || b := !bp.options.whitelist && matched ||
bp.options.whitelist && !matched bp.options.whitelist && !matched
if b { 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 return b
} }
func (p *localBypass) IsWhitelist() bool {
return p.options.whitelist
}
func (bp *localBypass) parseLine(s string) string { func (bp *localBypass) parseLine(s string) string {
if n := strings.IndexByte(s, '#'); n >= 0 { if n := strings.IndexByte(s, '#'); n >= 0 {
s = s[:n] s = s[:n]

View File

@ -75,3 +75,7 @@ func (p *grpcPlugin) Close() error {
} }
return nil return nil
} }
func (p *grpcPlugin) IsWhitelist() bool {
return false
}

View File

@ -96,3 +96,7 @@ func (p *httpPlugin) Contains(ctx context.Context, network, addr string, opts ..
} }
return res.OK 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 tr.Options().Route = rt
node = node.Copy() node = node.Copy()
node.Options().Transport = tr node.Options().Transport = tr
rt = NewRoute() rt = NewRoute(ChainRouteOption(c))
} }
rt.addNode(node) 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 { metrics.Labels{"chain": name, "node": node.Name}); v != nil {
v.Inc() v.Inc()
} }
} else { return
if marker != nil { }
marker.Reset()
} if marker != nil {
marker.Reset()
} }
} }
}() }()

View File

@ -297,9 +297,9 @@ type RedisRecorder struct {
} }
type RecorderObject struct { type RecorderObject struct {
Name string `json:"name"` Name string `json:"name"`
Record string `json:"record"` Record string `json:"record"`
Metadata map[string]any Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
} }
type LimiterConfig struct { type LimiterConfig struct {
@ -343,25 +343,32 @@ type HandlerConfig struct {
} }
type ForwarderConfig struct { 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"` Selector *SelectorConfig `yaml:",omitempty" json:"selector,omitempty"`
Nodes []*ForwardNodeConfig `json:"nodes"` Nodes []*ForwardNodeConfig `json:"nodes"`
} }
type ForwardNodeConfig struct { type ForwardNodeConfig struct {
Name string `yaml:",omitempty" json:"name,omitempty"` Name string `yaml:",omitempty" json:"name,omitempty"`
Addr string `yaml:",omitempty" json:"addr,omitempty"` Addr string `yaml:",omitempty" json:"addr,omitempty"`
Host string `yaml:",omitempty" json:"host,omitempty"` Network string `yaml:",omitempty" json:"network,omitempty"`
Network string `yaml:",omitempty" json:"network,omitempty"` Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
Protocol string `yaml:",omitempty" json:"protocol,omitempty"` Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
Path string `yaml:",omitempty" json:"path,omitempty"` // DEPRECATED by filter.protocol
Bypass string `yaml:",omitempty" json:"bypass,omitempty"` Protocol string `yaml:",omitempty" json:"protocol,omitempty"`
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"` // DEPRECATED by filter.host
HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"` Host string `yaml:",omitempty" json:"host,omitempty"`
TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"` // DEPRECATED by filter.path
// DEPRECATED by HTTP.Auth Path string `yaml:",omitempty" json:"path,omitempty"`
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"` // DEPRECATED by http.auth
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"` 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 { type HTTPURLRewriteConfig struct {
@ -369,11 +376,17 @@ type HTTPURLRewriteConfig struct {
Replacement 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 { type HTTPNodeConfig struct {
Host string `yaml:",omitempty" json:"host,omitempty"` Host string `yaml:",omitempty" json:"host,omitempty"`
Header map[string]string `yaml:",omitempty" json:"header,omitempty"` Header map[string]string `yaml:",omitempty" json:"header,omitempty"`
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
Rewrite []HTTPURLRewriteConfig `yaml:",omitempty" json:"rewrite,omitempty"` Rewrite []HTTPURLRewriteConfig `yaml:",omitempty" json:"rewrite,omitempty"`
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
} }
type TLSNodeConfig struct { type TLSNodeConfig struct {
@ -477,24 +490,21 @@ type HopConfig struct {
} }
type NodeConfig struct { type NodeConfig struct {
Name string `json:"name"` Name string `json:"name"`
Addr string `yaml:",omitempty" json:"addr,omitempty"` Addr string `yaml:",omitempty" json:"addr,omitempty"`
Host string `yaml:",omitempty" json:"host,omitempty"` Network string `yaml:",omitempty" json:"network,omitempty"`
Network string `yaml:",omitempty" json:"network,omitempty"` Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
Protocol string `yaml:",omitempty" json:"protocol,omitempty"` Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"`
Path string `yaml:",omitempty" json:"path,omitempty"` Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
Interface string `yaml:",omitempty" json:"interface,omitempty"` Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"` Connector *ConnectorConfig `yaml:",omitempty" json:"connector,omitempty"`
Bypass string `yaml:",omitempty" json:"bypass,omitempty"` Dialer *DialerConfig `yaml:",omitempty" json:"dialer,omitempty"`
Bypasses []string `yaml:",omitempty" json:"bypasses,omitempty"` Interface string `yaml:",omitempty" json:"interface,omitempty"`
Resolver string `yaml:",omitempty" json:"resolver,omitempty"` SockOpts *SockOptsConfig `yaml:"sockopts,omitempty" json:"sockopts,omitempty"`
Hosts string `yaml:",omitempty" json:"hosts,omitempty"` Filter *NodeFilterConfig `yaml:",omitempty" json:"filter,omitempty"`
Connector *ConnectorConfig `yaml:",omitempty" json:"connector,omitempty"` HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"`
Dialer *DialerConfig `yaml:",omitempty" json:"dialer,omitempty"` TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"`
HTTP *HTTPNodeConfig `yaml:",omitempty" json:"http,omitempty"` Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
TLS *TLSNodeConfig `yaml:",omitempty" json:"tls,omitempty"`
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
} }
type Config struct { type Config struct {

View File

@ -32,7 +32,7 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) {
} }
} }
switch strings.ToLower(cfg.Plugin.Type) { switch strings.ToLower(cfg.Plugin.Type) {
case "http": case plugin.HTTP:
return hop_plugin.NewHTTPPlugin( return hop_plugin.NewHTTPPlugin(
cfg.Name, cfg.Plugin.Addr, cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg), plugin.TLSConfigOption(tlsCfg),
@ -67,15 +67,17 @@ func ParseHop(cfg *config.HopConfig, log logger.Logger) (hop.Hop, error) {
} }
if v.Connector == nil { if v.Connector == nil {
v.Connector = &config.ConnectorConfig{ v.Connector = &config.ConnectorConfig{}
Type: "http", }
} if strings.TrimSpace(v.Connector.Type) == "" {
v.Connector.Type = "http"
} }
if v.Dialer == nil { if v.Dialer == nil {
v.Dialer = &config.DialerConfig{ v.Dialer = &config.DialerConfig{}
Type: "tcp", }
} if strings.TrimSpace(v.Dialer.Type) == "" {
v.Dialer.Type = "tcp"
} }
node, err := node_parser.ParseNode(cfg.Name, v, log) node, err := node_parser.ParseNode(cfg.Name, v, log)

View File

@ -147,46 +147,47 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
chain.TimeoutTransportOption(10*time.Second), 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{ opts := []chain.NodeOption{
chain.TransportNodeOption(tr), chain.TransportNodeOption(tr),
chain.BypassNodeOption(bypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)), chain.BypassNodeOption(bypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
chain.ResoloverNodeOption(registry.ResolverRegistry().Get(cfg.Resolver)), chain.ResoloverNodeOption(registry.ResolverRegistry().Get(cfg.Resolver)),
chain.HostMapperNodeOption(registry.HostsRegistry().Get(cfg.Hosts)), chain.HostMapperNodeOption(registry.HostsRegistry().Get(cfg.Hosts)),
chain.MetadataNodeOption(nm), chain.MetadataNodeOption(nm),
chain.HostNodeOption(host),
chain.ProtocolNodeOption(cfg.Protocol),
chain.PathNodeOption(cfg.Path),
chain.NetworkNodeOption(cfg.Network), 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 { if cfg.HTTP != nil {
settings := &chain.HTTPNodeSettings{ settings := &chain.HTTPNodeSettings{
Host: cfg.HTTP.Host, Host: cfg.HTTP.Host,
Header: cfg.HTTP.Header, Header: cfg.HTTP.Header,
} }
auth := cfg.HTTP.Auth if auth := cfg.HTTP.Auth; auth != nil && auth.Username != "" {
if auth == nil {
auth = cfg.Auth
}
if auth != nil {
settings.Auther = xauth.NewAuthenticator( settings.Auther = xauth.NewAuthenticator(
xauth.AuthsOption(map[string]string{auth.Username: auth.Password}), xauth.AuthsOption(map[string]string{auth.Username: auth.Password}),
xauth.LoggerOption(log.WithFields(map[string]any{ xauth.LoggerOption(log.WithFields(map[string]any{
"kind": "node", "kind": "node",
"node": cfg.Name, "node": cfg.Name,
"addr": cfg.Addr, "addr": cfg.Addr,
"host": cfg.Host,
"protocol": cfg.Protocol,
})), })),
) )
} }
@ -200,6 +201,7 @@ func ParseNode(hop string, cfg *config.NodeConfig, log logger.Logger) (*chain.No
} }
opts = append(opts, chain.HTTPNodeOption(settings)) opts = append(opts, chain.HTTPNodeOption(settings))
} }
if cfg.TLS != nil { if cfg.TLS != nil {
tlsCfg := &chain.TLSNodeSettings{ tlsCfg := &chain.TLSNodeSettings{
ServerName: cfg.TLS.ServerName, ServerName: cfg.TLS.ServerName,

View File

@ -2,6 +2,8 @@ package service
import ( import (
"fmt" "fmt"
"strings"
"time"
"github.com/go-gost/core/admission" "github.com/go-gost/core/admission"
"github.com/go-gost/core/auth" "github.com/go-gost/core/auth"
@ -34,14 +36,17 @@ import (
func ParseService(cfg *config.ServiceConfig) (service.Service, error) { func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
if cfg.Listener == nil { if cfg.Listener == nil {
cfg.Listener = &config.ListenerConfig{ cfg.Listener = &config.ListenerConfig{}
Type: "tcp",
}
} }
if strings.TrimSpace(cfg.Listener.Type) == "" {
cfg.Listener.Type = "tcp"
}
if cfg.Handler == nil { if cfg.Handler == nil {
cfg.Handler = &config.HandlerConfig{ cfg.Handler = &config.HandlerConfig{}
Type: "auto", }
} if strings.TrimSpace(cfg.Handler.Type) == "" {
cfg.Handler.Type = "auto"
} }
log := logger.Default() log := logger.Default()
@ -98,6 +103,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
var preUp, preDown, postUp, postDown []string var preUp, preDown, postUp, postDown []string
var ignoreChain bool var ignoreChain bool
var pStats *stats.Stats var pStats *stats.Stats
var observePeriod time.Duration
if cfg.Metadata != nil { if cfg.Metadata != nil {
md := metadata.NewMetadata(cfg.Metadata) md := metadata.NewMetadata(cfg.Metadata)
ppv = mdutil.GetInt(md, parsing.MDKeyProxyProtocol) ppv = mdutil.GetInt(md, parsing.MDKeyProxyProtocol)
@ -118,6 +124,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
if mdutil.GetBool(md, parsing.MDKeyEnableStats) { if mdutil.GetBool(md, parsing.MDKeyEnableStats) {
pStats = &stats.Stats{} pStats = &stats.Stats{}
} }
observePeriod = mdutil.GetDuration(md, "observePeriod")
} }
listenOpts := []listener.Option{ listenOpts := []listener.Option{
@ -143,7 +150,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
if rf := registry.ListenerRegistry().Get(cfg.Listener.Type); rf != nil { if rf := registry.ListenerRegistry().Get(cfg.Listener.Type); rf != nil {
ln = rf(listenOpts...) ln = rf(listenOpts...)
} else { } 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 { if cfg.Listener.Metadata == nil {
@ -230,7 +237,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
handler.ServiceOption(cfg.Name), handler.ServiceOption(cfg.Name),
) )
} else { } 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 { if forwarder, ok := h.(handler.Forwarder); ok {
@ -259,6 +266,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
xservice.RecordersOption(recorders...), xservice.RecordersOption(recorders...),
xservice.StatsOption(pStats), xservice.StatsOption(pStats),
xservice.ObserverOption(registry.ObserverRegistry().Get(cfg.Observer)), xservice.ObserverOption(registry.ObserverRegistry().Get(cfg.Observer)),
xservice.ObservePeriodOption(observePeriod),
xservice.LoggerOption(serviceLogger), xservice.LoggerOption(serviceLogger),
) )
@ -271,6 +279,14 @@ func parseForwarder(cfg *config.ForwarderConfig, log logger.Logger) (hop.Hop, er
return nil, nil return nil, nil
} }
hopName := cfg.Hop
if hopName == "" {
hopName = cfg.Name
}
if hopName != "" {
return registry.HopRegistry().Get(hopName), nil
}
hc := config.HopConfig{ hc := config.HopConfig{
Name: cfg.Name, Name: cfg.Name,
Selector: cfg.Selector, Selector: cfg.Selector,
@ -286,27 +302,42 @@ func parseForwarder(cfg *config.ForwarderConfig, log logger.Logger) (hop.Hop, er
if i > 0 { if i > 0 {
name = fmt.Sprintf("%s-%d", node.Name, i) 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{ hc.Nodes = append(hc.Nodes, &config.NodeConfig{
Name: name, Name: name,
Addr: addr, Addr: addr,
Host: node.Host,
Network: node.Network, Network: node.Network,
Protocol: node.Protocol,
Path: node.Path,
Bypass: node.Bypass, Bypass: node.Bypass,
Bypasses: node.Bypasses, Bypasses: node.Bypasses,
HTTP: node.HTTP, Filter: filter,
HTTP: httpCfg,
TLS: node.TLS, TLS: node.TLS,
Auth: node.Auth,
Metadata: node.Metadata, Metadata: node.Metadata,
}) })
} }
} }
} }
if len(hc.Nodes) > 0 { return hop_parser.ParseHop(&hc, log)
return hop_parser.ParseHop(&hc, log)
}
return registry.HopRegistry().Get(hc.Name), nil
} }
func chainGroup(name string, group *config.ChainGroupConfig) chain.Chainer { func chainGroup(name string, group *config.ChainGroupConfig) chain.Chainer {

View File

@ -70,6 +70,19 @@ func (d *http2Dialer) Dial(ctx context.Context, address string, opts ...dialer.D
opt(&options) 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{ client = &http.Client{
Transport: &http.Transport{ Transport: &http.Transport{
TLSClientConfig: d.options.TLSConfig, 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) return netd.Dial(ctx, network, addr)
}, },
ForceAttemptHTTP2: true, ForceAttemptHTTP2: true,
MaxIdleConns: 100, MaxIdleConns: 16,
IdleConnTimeout: 90 * time.Second, IdleConnTimeout: 30 * time.Second,
TLSHandshakeTimeout: 10 * time.Second, TLSHandshakeTimeout: 30 * time.Second,
ExpectContinueTimeout: 1 * time.Second, ExpectContinueTimeout: 15 * time.Second,
}, },
} }
d.clients[address] = client d.clients[address] = client
} }
var c net.Conn var c net.Conn = &conn{
c = &conn{
localAddr: &net.TCPAddr{}, localAddr: &net.TCPAddr{},
remoteAddr: raddr, remoteAddr: raddr,
onClose: func() { 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) return quic.DialEarly(context.Background(), udpConn.(net.PacketConn), udpAddr, tlsCfg, cfg)
}, },
QuicConfig: &quic.Config{ QUICConfig: &quic.Config{
KeepAlivePeriod: d.md.keepAlivePeriod, KeepAlivePeriod: d.md.keepAlivePeriod,
HandshakeIdleTimeout: d.md.handshakeTimeout, HandshakeIdleTimeout: d.md.handshakeTimeout,
MaxIdleTimeout: d.md.maxIdleTimeout, MaxIdleTimeout: d.md.maxIdleTimeout,

View File

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

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.KeepAlive = mdutil.GetInt(md, "kcp.keepalive")
d.md.config.Interval = mdutil.GetInt(md, "kcp.interval") d.md.config.Interval = mdutil.GetInt(md, "kcp.interval")
d.md.config.MTU = mdutil.GetInt(md, "kcp.mtu") 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.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) d.md.handshakeTimeout = mdutil.GetDuration(md, handshakeTimeout)
return return

View File

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

View File

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

View File

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

View File

@ -1,11 +1,14 @@
package ssh package ssh
import ( import (
"fmt"
"os" "os"
"time" "time"
mdata "github.com/go-gost/core/metadata" mdata "github.com/go-gost/core/metadata"
mdutil "github.com/go-gost/core/metadata/util" mdutil "github.com/go-gost/core/metadata/util"
"github.com/mitchellh/go-homedir"
"github.com/zalando/go-keyring"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
) )
@ -20,21 +23,35 @@ type metadata struct {
func (d *sshDialer) parseMetadata(md mdata.Metadata) (err error) { func (d *sshDialer) parseMetadata(md mdata.Metadata) (err error) {
const ( const (
handshakeTimeout = "handshakeTimeout" handshakeTimeout = "handshakeTimeout"
privateKeyFile = "privateKeyFile" privateKeyFile = "privateKeyFile"
passphrase = "passphrase" passphrase = "passphrase"
passphraseFromKeyring = "passphraseFromKeyring"
) )
if key := mdutil.GetString(md, privateKeyFile); key != "" { if key := mdutil.GetString(md, privateKeyFile); key != "" {
key, err = homedir.Expand(key)
if err != nil {
return err
}
data, err := os.ReadFile(key) data, err := os.ReadFile(key)
if err != nil { if err != nil {
return err return err
} }
if pp := mdutil.GetString(md, passphrase); pp != "" { var pp string
d.md.signer, err = ssh.ParsePrivateKeyWithPassphrase(data, []byte(pp)) 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 { } else {
pp = mdutil.GetString(md, passphrase)
}
if pp == "" {
d.md.signer, err = ssh.ParsePrivateKey(data) d.md.signer, err = ssh.ParsePrivateKey(data)
} else {
d.md.signer, err = ssh.ParsePrivateKeyWithPassphrase(data, []byte(pp))
} }
if err != nil { if err != nil {
return err return err

View File

@ -1,11 +1,14 @@
package sshd package sshd
import ( import (
"fmt"
"os" "os"
"time" "time"
mdata "github.com/go-gost/core/metadata" mdata "github.com/go-gost/core/metadata"
mdutil "github.com/go-gost/core/metadata/util" mdutil "github.com/go-gost/core/metadata/util"
"github.com/mitchellh/go-homedir"
"github.com/zalando/go-keyring"
"golang.org/x/crypto/ssh" "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 != "" { if key := mdutil.GetString(md, privateKeyFile); key != "" {
key, err = homedir.Expand(key)
if err != nil {
return err
}
data, err := os.ReadFile(key) data, err := os.ReadFile(key)
if err != nil { if err != nil {
return err 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 == "" { if pp == "" {
d.md.signer, err = ssh.ParsePrivateKey(data) d.md.signer, err = ssh.ParsePrivateKey(data)
} else { } else {

58
go.mod
View File

@ -1,13 +1,15 @@
module github.com/go-gost/x module github.com/go-gost/x
go 1.21 go 1.22
toolchain go1.22.2
require ( require (
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
github.com/gin-contrib/cors v1.5.0 github.com/gin-contrib/cors v1.5.0
github.com/gin-gonic/gin v1.9.1 github.com/gin-gonic/gin v1.9.1
github.com/go-gost/core v0.0.0-20240131151724-a06608ccafbf github.com/go-gost/core v0.0.0-20240508132029-8d554ddcf77c
github.com/go-gost/gosocks4 v0.0.1 github.com/go-gost/gosocks4 v0.0.1
github.com/go-gost/gosocks5 v0.4.0 github.com/go-gost/gosocks5 v0.4.0
github.com/go-gost/plugin v0.0.0-20240103125338-9c84e29cb81a github.com/go-gost/plugin v0.0.0-20240103125338-9c84e29cb81a
@ -16,16 +18,16 @@ require (
github.com/go-redis/redis/v8 v8.11.5 github.com/go-redis/redis/v8 v8.11.5
github.com/gobwas/glob v0.2.3 github.com/gobwas/glob v0.2.3
github.com/golang/snappy v0.0.4 github.com/golang/snappy v0.0.4
github.com/google/uuid v1.4.0 github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.1 github.com/gorilla/websocket v1.5.1
github.com/miekg/dns v1.1.57 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/patrickmn/go-cache v2.1.0+incompatible
github.com/pion/dtls/v2 v2.2.6 github.com/pion/dtls/v2 v2.2.6
github.com/pires/go-proxyproto v0.7.0 github.com/pires/go-proxyproto v0.7.0
github.com/prometheus/client_golang v1.17.0 github.com/prometheus/client_golang v1.19.1
github.com/quic-go/quic-go v0.40.1 github.com/quic-go/quic-go v0.45.0
github.com/quic-go/webtransport-go v0.6.0 github.com/quic-go/webtransport-go v0.8.0
github.com/refraction-networking/utls v1.5.4
github.com/rs/xid v1.3.0 github.com/rs/xid v1.3.0
github.com/shadowsocks/go-shadowsocks2 v0.1.5 github.com/shadowsocks/go-shadowsocks2 v0.1.5
github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601 github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601
@ -37,25 +39,28 @@ require (
github.com/xtaci/smux v1.5.24 github.com/xtaci/smux v1.5.24
github.com/xtaci/tcpraw v1.2.25 github.com/xtaci/tcpraw v1.2.25
github.com/yl2chen/cidranger v1.0.2 github.com/yl2chen/cidranger v1.0.2
golang.org/x/crypto v0.17.0 github.com/zalando/go-keyring v0.2.4
golang.org/x/net v0.19.0 golang.org/x/crypto v0.24.0
golang.org/x/sys v0.15.0 golang.org/x/net v0.26.0
golang.org/x/sys v0.21.0
golang.org/x/time v0.5.0 golang.org/x/time v0.5.0
golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478 golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478
google.golang.org/grpc v1.59.0 google.golang.org/grpc v1.64.0
google.golang.org/protobuf v1.31.0 google.golang.org/protobuf v1.34.2
gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )
require ( require (
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect 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/beorn7/perks v1.0.1 // indirect
github.com/bytedance/sonic v1.10.2 // indirect github.com/bytedance/sonic v1.10.2 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.1 // indirect github.com/chenzhuoyu/iasm v0.9.1 // indirect
github.com/coreos/go-iptables v0.5.0 // 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/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect
@ -63,11 +68,11 @@ require (
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.16.0 // indirect github.com/go-playground/validator/v10 v10.16.0 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/goccy/go-json v0.10.2 // 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/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/hashicorp/hcl v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/klauspost/cpuid/v2 v2.2.6 // indirect
@ -75,21 +80,19 @@ require (
github.com/leodido/go-urn v1.2.4 // indirect github.com/leodido/go-urn v1.2.4 // indirect
github.com/magiconair/properties v1.8.7 // indirect github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/onsi/ginkgo/v2 v2.12.0 // indirect github.com/onsi/ginkgo/v2 v2.19.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.1 // indirect github.com/pelletier/go-toml/v2 v2.1.1 // indirect
github.com/pion/logging v0.2.2 // indirect github.com/pion/logging v0.2.2 // indirect
github.com/pion/transport/v2 v2.0.2 // indirect github.com/pion/transport/v2 v2.0.2 // indirect
github.com/pion/udp/v2 v2.0.1 // indirect github.com/pion/udp/v2 v2.0.1 // indirect
github.com/pkg/errors v0.9.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/client_model v0.5.0 // indirect
github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/common v0.48.0 // indirect
github.com/prometheus/procfs v0.11.1 // indirect github.com/prometheus/procfs v0.12.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect
@ -104,14 +107,15 @@ require (
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect github.com/ugorji/go/codec v1.2.11 // indirect
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect
go.uber.org/mock v0.3.0 // indirect go.uber.org/mock v0.4.0 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.6.0 // indirect golang.org/x/arch v0.6.0 // indirect
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 // indirect golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 // indirect
golang.org/x/mod v0.14.0 // indirect golang.org/x/mod v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/sync v0.7.0 // indirect
golang.org/x/tools v0.16.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 golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
) )

132
go.sum
View File

@ -4,6 +4,8 @@ github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmH
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA= 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 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= 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 h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ=
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= 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 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@ -26,6 +28,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/coreos/go-iptables v0.5.0 h1:mw6SAibtHKZcNzAsOxjoHIG0gy5YFHhypWSSNc6EjbQ= github.com/coreos/go-iptables v0.5.0 h1:mw6SAibtHKZcNzAsOxjoHIG0gy5YFHhypWSSNc6EjbQ=
github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= 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.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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 h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
@ -49,8 +53,8 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-gost/core v0.0.0-20240131151724-a06608ccafbf h1:akQ96Ibm+P7IftDluZPoMCzBzbLR/TjFu8Wpjy3H7hM= github.com/go-gost/core v0.0.0-20240508132029-8d554ddcf77c h1:1ahtn+3bQB50at5ubWDOrA4yja8vWpWNrGSRaCztNWg=
github.com/go-gost/core v0.0.0-20240131151724-a06608ccafbf/go.mod h1:ndkgWVYRLwupVaFFWv8ML1Nr8tD3xhHK245PLpUDg4E= github.com/go-gost/core v0.0.0-20240508132029-8d554ddcf77c/go.mod h1:j08tDHkFzk7dfOeLhl3RWsASdf9YWWRfWBUQqbQvx3A=
github.com/go-gost/gosocks4 v0.0.1 h1:+k1sec8HlELuQV7rWftIkmy8UijzUt2I6t+iMPlGB2s= 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/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 h1:EIrOEkpJez4gwHrMa33frA+hHXJyevjp47thpMQsJzI=
@ -61,8 +65,8 @@ 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/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 h1:xj8gUZGYO3nb5+6Bjw9+tsFkA9sYynrOvDvvC4uDV2I=
github.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451/go.mod h1:/9QfdewqmHdaE362Hv5nDaSWLx3pCmtD870d6GaquXs= 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.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 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 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@ -73,12 +77,14 @@ github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqR
github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= 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 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= 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/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
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/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= 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/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 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 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/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -90,25 +96,21 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 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.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.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 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 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.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.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.0/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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 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 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/pprof v0.0.0-20230912144702-c363fe2c2ed8 h1:gpptm606MZYGaMHMsB4Srmb6EbW/IVHnt04rcMXnkBQ= github.com/google/pprof v0.0.0-20240528025155-186aa0362fba h1:ql1qNgCyOB7iAEk8JTNM+zJrgIbnyCKX/wdlyPufP5g=
github.com/google/pprof v0.0.0-20230912144702-c363fe2c2ed8/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/pprof v0.0.0-20240528025155-186aa0362fba/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= 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 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
@ -131,10 +133,10 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20/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.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= github.com/miekg/dns v1.1.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 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -146,10 +148,10 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= 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 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= 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.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA=
github.com/onsi/ginkgo/v2 v2.12.0/go.mod h1:ZNEzXISYlqpb8S36iN71ifqLi3vVD1rVJGvWRCJOUpQ= github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= 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 h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
@ -169,24 +171,21 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= 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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= 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.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.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
github.com/prometheus/procfs v0.11.1 h1:xRC8Iq1yyca5ypa9n1EZnWZkt7dwcoRPQwX/5gwaUuI= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY= 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 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=
github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=
github.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs= github.com/quic-go/quic-go v0.45.0 h1:OHmkQGM37luZITyTSu6ff03HP/2IrwDX1ZFiNEhSFUE=
github.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= github.com/quic-go/quic-go v0.45.0/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI=
github.com/quic-go/quic-go v0.40.1 h1:X3AGzUNFs0jVuO3esAGnTfvdgvL4fq655WaOi1snv1Q= github.com/quic-go/webtransport-go v0.8.0 h1:HxSrwun11U+LlmwpgM1kEqIqH90IT4N8auv/cD7QFJg=
github.com/quic-go/quic-go v0.40.1/go.mod h1:PeN7kuVJ4xZbxSv/4OX6S1USOX8MJvydwpTx31vx60c= github.com/quic-go/webtransport-go v0.8.0/go.mod h1:N99tjprW432Ut5ONql/aUhSLT0YVSlwHohQsuac9WaM=
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/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= 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/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
@ -217,11 +216,11 @@ github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= github.com/spf13/viper v1.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.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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.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.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.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
@ -256,8 +255,10 @@ github.com/xtaci/tcpraw v1.2.25/go.mod h1:dKyZ2V75s0cZ7cbgJYdxPvms7af0joIeOyx1Gg
github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU= github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU=
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g= github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo= github.com/zalando/go-keyring v0.2.4 h1:wi2xxTqdiwMKbM6TWwi+uJCG/Tum2UV0jqaQhCa9/68=
go.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= 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 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 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.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
@ -270,19 +271,19 @@ golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= 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-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848 h1:+iq7lrkxmFNBM7xx+Rae2W6uyPfhPeDWD+n+JgppptE= golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 h1:LoYXNGAShUG3m/ehNk4iFctuhGX/+R1ZpfJ4/ia80JM=
golang.org/x/exp v0.0.0-20231219180239-dc181d75b848/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= 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-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-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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-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-20180826012351-8a410e7b638d/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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -294,16 +295,15 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.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.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.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= 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-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-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-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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 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-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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -317,22 +317,22 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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-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-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.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.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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= 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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.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.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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 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 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 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-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -343,8 +343,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= 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-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-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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -356,24 +356,22 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I= google.golang.org/genproto/googleapis/rpc v0.0.0-20240610135401-a8a62080eff3 h1:9Xyg6I9IWQZhRVfCWjKK+l6kI0jHcPesVlMnT//aHNo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= 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.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 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.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= 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-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-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 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.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.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.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.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

View File

@ -8,6 +8,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"hash/crc32" "hash/crc32"
"io"
"net" "net"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
@ -148,7 +149,7 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
fields := map[string]any{ fields := map[string]any{
"dst": addr, "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 fields["user"] = u
} }
log = log.WithFields(fields) log = log.WithFields(fields)
@ -222,26 +223,6 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
} }
defer cc.Close() 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 := traffic_wrapper.WrapReadWriter(h.options.Limiter, conn, rw := traffic_wrapper.WrapReadWriter(h.options.Limiter, conn,
traffic.NetworkOption(network), traffic.NetworkOption(network),
traffic.AddrOption(addr), traffic.AddrOption(addr),
@ -256,6 +237,22 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
rw = stats_wrapper.WrapReadWriter(rw, pstats) 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() start := time.Now()
log.Infof("%s <-> %s", conn.RemoteAddr(), addr) log.Infof("%s <-> %s", conn.RemoteAddr(), addr)
netpkg.Transport(rw, cc) netpkg.Transport(rw, cc)
@ -266,6 +263,49 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
return nil 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) { func (h *httpHandler) decodeServerName(s string) (string, error) {
b, err := base64.RawURLEncoding.DecodeString(s) b, err := base64.RawURLEncoding.DecodeString(s)
if err != nil { if err != nil {
@ -284,7 +324,7 @@ func (h *httpHandler) decodeServerName(s string) (string, error) {
return string(v), nil 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 == "" { if proxyAuth == "" {
return return
} }
@ -306,7 +346,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) { 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 { if h.options.Auther == nil {
return "", true return "", true
} }
@ -412,7 +452,11 @@ func (h *httpHandler) observeStats(ctx context.Context) {
return return
} }
ticker := time.NewTicker(5 * time.Second) d := h.md.observePeriod
if d < time.Millisecond {
d = 5 * time.Second
}
ticker := time.NewTicker(d)
defer ticker.Stop() defer ticker.Stop()
for { for {

View File

@ -3,6 +3,7 @@ package http
import ( import (
"net/http" "net/http"
"strings" "strings"
"time"
mdata "github.com/go-gost/core/metadata" mdata "github.com/go-gost/core/metadata"
mdutil "github.com/go-gost/core/metadata/util" mdutil "github.com/go-gost/core/metadata/util"
@ -18,20 +19,11 @@ type metadata struct {
header http.Header header http.Header
hash string hash string
authBasicRealm string authBasicRealm string
observePeriod time.Duration
} }
func (h *httpHandler) parseMetadata(md mdata.Metadata) error { func (h *httpHandler) parseMetadata(md mdata.Metadata) error {
const ( if m := mdutil.GetStringMapString(md, "http.header", "header"); len(m) > 0 {
header = "header"
probeResistKey = "probeResistance"
probeResistKeyX = "probe_resist"
knock = "knock"
enableUDP = "udp"
hash = "hash"
authBasicRealm = "authBasicRealm"
)
if m := mdutil.GetStringMapString(md, header); len(m) > 0 {
hd := http.Header{} hd := http.Header{}
for k, v := range m { for k, v := range m {
hd.Add(k, v) hd.Add(k, v)
@ -39,22 +31,20 @@ func (h *httpHandler) parseMetadata(md mdata.Metadata) error {
h.md.header = hd h.md.header = hd
} }
pr := mdutil.GetString(md, probeResistKey) if pr := mdutil.GetString(md, "probeResist", "probe_resist"); pr != "" {
if pr == "" {
pr = mdutil.GetString(md, probeResistKeyX)
}
if pr != "" {
if ss := strings.SplitN(pr, ":", 2); len(ss) == 2 { if ss := strings.SplitN(pr, ":", 2); len(ss) == 2 {
h.md.probeResistance = &probeResistance{ h.md.probeResistance = &probeResistance{
Type: ss[0], Type: ss[0],
Value: ss[1], Value: ss[1],
Knock: mdutil.GetString(md, knock), Knock: mdutil.GetString(md, "knock"),
} }
} }
} }
h.md.enableUDP = mdutil.GetBool(md, enableUDP) h.md.enableUDP = mdutil.GetBool(md, "udp")
h.md.hash = mdutil.GetString(md, hash) h.md.hash = mdutil.GetString(md, "hash")
h.md.authBasicRealm = mdutil.GetString(md, authBasicRealm) h.md.authBasicRealm = mdutil.GetString(md, "authBasicRealm")
h.md.observePeriod = mdutil.GetDuration(md, "observePeriod")
return nil return nil
} }

View File

@ -419,7 +419,11 @@ func (h *http2Handler) observeStats(ctx context.Context) {
return return
} }
ticker := time.NewTicker(5 * time.Second) d := h.md.observePeriod
if d < time.Millisecond {
d = 5 * time.Second
}
ticker := time.NewTicker(d)
defer ticker.Stop() defer ticker.Stop()
for { for {

View File

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

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 package redirect
@ -7,7 +7,7 @@ import (
"net" "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") err = errors.New("TCP redirect is not available on non-linux platform")
return return
} }

View File

@ -204,7 +204,11 @@ func (h *relayHandler) observeStats(ctx context.Context) {
return return
} }
ticker := time.NewTicker(5 * time.Second) d := h.md.observePeriod
if d < time.Millisecond {
d = 5 * time.Second
}
ticker := time.NewTicker(d)
defer ticker.Stop() defer ticker.Stop()
for { for {

View File

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

View File

@ -14,9 +14,9 @@ import (
"github.com/go-gost/gosocks4" "github.com/go-gost/gosocks4"
ctxvalue "github.com/go-gost/x/ctx" ctxvalue "github.com/go-gost/x/ctx"
netpkg "github.com/go-gost/x/internal/net" 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" "github.com/go-gost/x/limiter/traffic/wrapper"
"github.com/go-gost/x/registry" "github.com/go-gost/x/registry"
stats_util "github.com/go-gost/x/internal/util/stats"
"github.com/go-gost/x/stats" "github.com/go-gost/x/stats"
stats_wrapper "github.com/go-gost/x/stats/wrapper" stats_wrapper "github.com/go-gost/x/stats/wrapper"
) )
@ -218,7 +218,11 @@ func (h *socks4Handler) observeStats(ctx context.Context) {
return return
} }
ticker := time.NewTicker(5 * time.Second) d := h.md.observePeriod
if d < time.Millisecond {
d = 5 * time.Second
}
ticker := time.NewTicker(d)
defer ticker.Stop() defer ticker.Stop()
for { for {

View File

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

View File

@ -160,7 +160,11 @@ func (h *socks5Handler) observeStats(ctx context.Context) {
return return
} }
ticker := time.NewTicker(5 * time.Second) d := h.md.observePeriod
if d < time.Millisecond {
d = 5 * time.Second
}
ticker := time.NewTicker(d)
defer ticker.Stop() defer ticker.Stop()
for { for {

View File

@ -18,32 +18,23 @@ type metadata struct {
compatibilityMode bool compatibilityMode bool
hash string hash string
muxCfg *mux.Config muxCfg *mux.Config
observePeriod time.Duration
} }
func (h *socks5Handler) parseMetadata(md mdata.Metadata) (err error) { func (h *socks5Handler) parseMetadata(md mdata.Metadata) (err error) {
const ( h.md.readTimeout = mdutil.GetDuration(md, "readTimeout")
readTimeout = "readTimeout" h.md.noTLS = mdutil.GetBool(md, "notls")
noTLS = "notls" h.md.enableBind = mdutil.GetBool(md, "bind")
enableBind = "bind" h.md.enableUDP = mdutil.GetBool(md, "udp")
enableUDP = "udp"
udpBufferSize = "udpBufferSize"
compatibilityMode = "comp"
hash = "hash"
)
h.md.readTimeout = mdutil.GetDuration(md, readTimeout) if bs := mdutil.GetInt(md, "udpBufferSize"); bs > 0 {
h.md.noTLS = mdutil.GetBool(md, noTLS)
h.md.enableBind = mdutil.GetBool(md, enableBind)
h.md.enableUDP = mdutil.GetBool(md, enableUDP)
if bs := mdutil.GetInt(md, udpBufferSize); bs > 0 {
h.md.udpBufferSize = int(math.Min(math.Max(float64(bs), 512), 64*1024)) h.md.udpBufferSize = int(math.Min(math.Max(float64(bs), 512), 64*1024))
} else { } else {
h.md.udpBufferSize = 4096 h.md.udpBufferSize = 4096
} }
h.md.compatibilityMode = mdutil.GetBool(md, compatibilityMode) h.md.compatibilityMode = mdutil.GetBool(md, "comp")
h.md.hash = mdutil.GetString(md, hash) h.md.hash = mdutil.GetString(md, "hash")
h.md.muxCfg = &mux.Config{ h.md.muxCfg = &mux.Config{
Version: mdutil.GetInt(md, "mux.version"), Version: mdutil.GetInt(md, "mux.version"),
@ -55,5 +46,7 @@ func (h *socks5Handler) parseMetadata(md mdata.Metadata) (err error) {
MaxStreamBuffer: mdutil.GetInt(md, "mux.maxStreamBuffer"), MaxStreamBuffer: mdutil.GetInt(md, "mux.maxStreamBuffer"),
} }
h.md.observePeriod = mdutil.GetDuration(md, "observePeriod")
return nil return nil
} }

View File

@ -12,7 +12,6 @@ import (
"github.com/go-gost/core/handler" "github.com/go-gost/core/handler"
"github.com/go-gost/core/hop" "github.com/go-gost/core/hop"
md "github.com/go-gost/core/metadata" md "github.com/go-gost/core/metadata"
"github.com/go-gost/core/router"
tun_util "github.com/go-gost/x/internal/util/tun" tun_util "github.com/go-gost/x/internal/util/tun"
"github.com/go-gost/x/registry" "github.com/go-gost/x/registry"
"github.com/songgao/water/waterutil" "github.com/songgao/water/waterutil"
@ -109,23 +108,6 @@ func (h *tunHandler) Handle(ctx context.Context, conn net.Conn, opts ...handler.
return h.handleServer(ctx, conn, config, log) return h.handleServer(ctx, conn, config, log)
} }
func (h *tunHandler) findRouteFor(ctx context.Context, dst net.IP, router router.Router) net.Addr {
if v, ok := h.routes.Load(ipToTunRouteKey(dst)); ok {
return v.(net.Addr)
}
if router == nil {
return nil
}
if route := router.GetRoute(ctx, dst); route != nil && route.Gateway != nil {
if v, ok := h.routes.Load(ipToTunRouteKey(route.Gateway)); ok {
return v.(net.Addr)
}
}
return nil
}
var mIPProts = map[waterutil.IPProtocol]string{ var mIPProts = map[waterutil.IPProtocol]string{
waterutil.HOPOPT: "HOPOPT", waterutil.HOPOPT: "HOPOPT",
waterutil.ICMP: "ICMP", waterutil.ICMP: "ICMP",

View File

@ -16,28 +16,23 @@ type metadata struct {
bufferSize int bufferSize int
keepAlivePeriod time.Duration keepAlivePeriod time.Duration
passphrase string passphrase string
p2p bool
} }
func (h *tunHandler) parseMetadata(md mdata.Metadata) (err error) { func (h *tunHandler) parseMetadata(md mdata.Metadata) (err error) {
const ( h.md.bufferSize = mdutil.GetInt(md, "tun.bufsize", "bufsize", "buffersize")
bufferSize = "bufferSize"
keepAlive = "keepAlive"
keepAlivePeriod = "ttl"
passphrase = "passphrase"
)
h.md.bufferSize = mdutil.GetInt(md, bufferSize)
if h.md.bufferSize <= 0 { if h.md.bufferSize <= 0 {
h.md.bufferSize = defaultBufferSize h.md.bufferSize = defaultBufferSize
} }
if mdutil.GetBool(md, keepAlive) { if mdutil.GetBool(md, "tun.keepalive", "keepalive") {
h.md.keepAlivePeriod = mdutil.GetDuration(md, keepAlivePeriod) h.md.keepAlivePeriod = mdutil.GetDuration(md, "tun.ttl", "ttl")
if h.md.keepAlivePeriod <= 0 { if h.md.keepAlivePeriod <= 0 {
h.md.keepAlivePeriod = defaultKeepAlivePeriod h.md.keepAlivePeriod = defaultKeepAlivePeriod
} }
} }
h.md.passphrase = mdutil.GetString(md, passphrase) h.md.passphrase = mdutil.GetString(md, "tun.token", "token", "passphrase")
h.md.p2p = mdutil.GetBool(md, "tun.p2p", "p2p")
return return
} }

View File

@ -10,6 +10,7 @@ import (
"github.com/go-gost/core/common/bufpool" "github.com/go-gost/core/common/bufpool"
"github.com/go-gost/core/logger" "github.com/go-gost/core/logger"
"github.com/go-gost/core/router"
tun_util "github.com/go-gost/x/internal/util/tun" tun_util "github.com/go-gost/x/internal/util/tun"
"github.com/songgao/water/waterutil" "github.com/songgao/water/waterutil"
"golang.org/x/net/ipv4" "golang.org/x/net/ipv4"
@ -203,11 +204,13 @@ func (h *tunHandler) transportServer(ctx context.Context, tun io.ReadWriter, con
return nil return nil
} }
if addr := h.findRouteFor(ctx, dst, config.Router); addr != nil { if !h.md.p2p {
log.Debugf("find route: %s -> %s", dst, addr) if addr := h.findRouteFor(ctx, dst, config.Router); addr != nil {
log.Debugf("find route: %s -> %s", dst, addr)
_, err := conn.WriteTo(b[:n], addr) _, err := conn.WriteTo(b[:n], addr)
return err return err
}
} }
if _, err := tun.Write(b[:n]); err != nil { if _, err := tun.Write(b[:n]); err != nil {
@ -231,6 +234,9 @@ func (h *tunHandler) transportServer(ctx context.Context, tun io.ReadWriter, con
} }
func (h *tunHandler) updateRoute(ip net.IP, addr net.Addr, log logger.Logger) { func (h *tunHandler) updateRoute(ip net.IP, addr net.Addr, log logger.Logger) {
if h.md.p2p {
ip = net.IPv6zero
}
rkey := ipToTunRouteKey(ip) rkey := ipToTunRouteKey(ip)
if actual, loaded := h.routes.LoadOrStore(rkey, addr); loaded { if actual, loaded := h.routes.LoadOrStore(rkey, addr); loaded {
if actual.(net.Addr).String() != addr.String() { if actual.(net.Addr).String() != addr.String() {
@ -242,3 +248,25 @@ func (h *tunHandler) updateRoute(ip net.IP, addr net.Addr, log logger.Logger) {
log.Debugf("new route: %s -> %s", ip, addr) log.Debugf("new route: %s -> %s", ip, addr)
} }
} }
func (h *tunHandler) findRouteFor(ctx context.Context, dst net.IP, router router.Router) net.Addr {
if h.md.p2p {
dst = net.IPv6zero
router = nil
}
if v, ok := h.routes.Load(ipToTunRouteKey(dst)); ok {
return v.(net.Addr)
}
if router == nil {
return nil
}
if route := router.GetRoute(ctx, dst); route != nil && route.Gateway != nil {
if v, ok := h.routes.Load(ipToTunRouteKey(route.Gateway)); ok {
return v.(net.Addr)
}
}
return nil
}

View File

@ -1,7 +1,6 @@
package hop package hop
import ( import (
"bytes"
"context" "context"
"encoding/json" "encoding/json"
"io" "io"
@ -181,7 +180,11 @@ func (p *chainHop) filterByHost(host string, nodes ...*chain.Node) (filters []*c
if node == nil { if node == nil {
continue continue
} }
vhost := node.Options().Host
var vhost string
if filter := node.Options().Filter; filter != nil {
vhost = filter.Host
}
if vhost == "" { // backup node if vhost == "" { // backup node
if !found { if !found {
filters = append(filters, node) filters = append(filters, node)
@ -216,14 +219,18 @@ func (p *chainHop) filterByProtocol(protocol string, nodes ...*chain.Node) (filt
continue continue
} }
if node.Options().Protocol == "" { var prot string
if filter := node.Options().Filter; filter != nil {
prot = filter.Protocol
}
if prot == "" {
if !found { if !found {
filters = append(filters, node) filters = append(filters, node)
} }
continue continue
} }
if node.Options().Protocol == protocol { if prot == protocol {
if !found { if !found {
filters = nil filters = nil
} }
@ -244,19 +251,31 @@ func (p *chainHop) filterByPath(path string, nodes ...*chain.Node) (filters []*c
p.options.logger.Debugf("filter by path: %s", path) p.options.logger.Debugf("filter by path: %s", path)
sort.SliceStable(nodes, func(i, j int) bool { sort.SliceStable(nodes, func(i, j int) bool {
return len(nodes[i].Options().Path) > len(nodes[j].Options().Path) filter1 := nodes[i].Options().Filter
if filter1 == nil {
return false
}
filter2 := nodes[j].Options().Filter
if filter2 == nil {
return true
}
return len(filter1.Path) > len(filter2.Path)
}) })
found := false found := false
for _, node := range nodes { for _, node := range nodes {
if node.Options().Path == "" { var pathFilter string
if filter := node.Options().Filter; filter != nil {
pathFilter = filter.Path
}
if pathFilter == "" {
if !found { if !found {
filters = append(filters, node) filters = append(filters, node)
} }
continue continue
} }
if strings.HasPrefix(path, node.Options().Path) { if strings.HasPrefix(path, pathFilter) {
if !found { if !found {
filters = nil filters = nil
} }
@ -308,33 +327,30 @@ func (p *chainHop) reload(ctx context.Context) (err error) {
} }
func (p *chainHop) load(ctx context.Context) (nodes []*chain.Node, err error) { func (p *chainHop) load(ctx context.Context) (nodes []*chain.Node, err error) {
if p.options.fileLoader != nil { if loader := p.options.fileLoader; loader != nil {
r, er := p.options.fileLoader.Load(ctx) r, er := loader.Load(ctx)
if er != nil { if er != nil {
p.options.logger.Warnf("file loader: %v", er) p.options.logger.Warnf("file loader: %v", er)
} }
nodes, _ = p.parseNode(r) nodes, _ = p.parseNode(r)
} }
if p.options.redisLoader != nil { if loader := p.options.redisLoader; loader != nil {
if lister, ok := p.options.redisLoader.(loader.Lister); ok { r, er := loader.Load(ctx)
list, er := lister.List(ctx) if er != nil {
if er != nil { p.options.logger.Warnf("redis loader: %v", er)
p.options.logger.Warnf("redis loader: %v", er)
}
for _, s := range list {
nl, _ := p.parseNode(bytes.NewReader([]byte(s)))
nodes = append(nodes, nl...)
}
} }
ns, _ := p.parseNode(r)
nodes = append(nodes, ns...)
} }
if p.options.httpLoader != nil {
r, er := p.options.httpLoader.Load(ctx) if loader := p.options.httpLoader; loader != nil {
r, er := loader.Load(ctx)
if er != nil { if er != nil {
p.options.logger.Warnf("http loader: %v", er) p.options.logger.Warnf("http loader: %v", er)
} }
if node, _ := p.parseNode(r); node != nil { if ns, _ := p.parseNode(r); ns != nil {
nodes = append(nodes, node...) nodes = append(nodes, ns...)
} }
} }
@ -342,6 +358,10 @@ func (p *chainHop) load(ctx context.Context) (nodes []*chain.Node, err error) {
} }
func (p *chainHop) parseNode(r io.Reader) ([]*chain.Node, error) { func (p *chainHop) parseNode(r io.Reader) ([]*chain.Node, error) {
if r == nil {
return nil, nil
}
var ncs []*config.NodeConfig var ncs []*config.NodeConfig
if err := json.NewDecoder(r).Decode(&ncs); err != nil { if err := json.NewDecoder(r).Decode(&ncs); err != nil {
return nil, err return nil, err

View File

@ -94,6 +94,10 @@ func (p *grpcPlugin) Select(ctx context.Context, opts ...hop.SelectOption) *chai
} }
func (p *grpcPlugin) Close() error { func (p *grpcPlugin) Close() error {
if p.conn == nil {
return nil
}
if closer, ok := p.conn.(io.Closer); ok { if closer, ok := p.conn.(io.Closer); ok {
return closer.Close() return closer.Close()
} }

View File

@ -71,6 +71,10 @@ func (p *grpcPlugin) Lookup(ctx context.Context, network, host string, opts ...h
} }
func (p *grpcPlugin) Close() error { func (p *grpcPlugin) Close() error {
if p.conn == nil {
return nil
}
if closer, ok := p.conn.(io.Closer); ok { if closer, ok := p.conn.(io.Closer); ok {
return closer.Close() return closer.Close()
} }

View File

@ -82,6 +82,10 @@ func (p *grpcPlugin) SetRule(ctx context.Context, rule *ingress.Rule, opts ...in
} }
func (p *grpcPlugin) Close() error { func (p *grpcPlugin) Close() error {
if p.conn == nil {
return nil
}
if closer, ok := p.conn.(io.Closer); ok { if closer, ok := p.conn.(io.Closer); ok {
return closer.Close() return closer.Close()
} }

View File

@ -12,6 +12,11 @@ import (
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"
) )
const (
GRPC string = "grpc"
HTTP string = "http"
)
type Options struct { type Options struct {
Token string Token string
TLSConfig *tls.Config TLSConfig *tls.Config

View File

@ -160,7 +160,7 @@ func NewHTTP3Server(addr string, quicConfig *quic.Config, opts ...ServerOption)
http3Server: &http3.Server{ http3Server: &http3.Server{
Addr: addr, Addr: addr,
TLSConfig: options.tlsConfig, TLSConfig: options.tlsConfig,
QuicConfig: quicConfig, QUICConfig: quicConfig,
}, },
cqueue: make(chan net.Conn, options.backlog), cqueue: make(chan net.Conn, options.backlog),
closed: make(chan struct{}), closed: make(chan struct{}),

View File

@ -95,6 +95,10 @@ func (p *grpcPlugin) Out(ctx context.Context, key string, opts ...traffic.Option
} }
func (p *grpcPlugin) Close() error { func (p *grpcPlugin) Close() error {
if p.conn == nil {
return nil
}
if closer, ok := p.conn.(io.Closer); ok { if closer, ok := p.conn.(io.Closer); ok {
return closer.Close() return closer.Close()
} }

View File

@ -58,7 +58,7 @@ func (l *http3Listener) Init(md md.Metadata) (err error) {
l.server = &http3.Server{ l.server = &http3.Server{
Addr: l.options.Addr, Addr: l.options.Addr,
TLSConfig: l.options.TLSConfig, TLSConfig: l.options.TLSConfig,
QuicConfig: &quic.Config{ QUICConfig: &quic.Config{
KeepAlivePeriod: l.md.keepAlivePeriod, KeepAlivePeriod: l.md.keepAlivePeriod,
HandshakeIdleTimeout: l.md.handshakeTimeout, HandshakeIdleTimeout: l.md.handshakeTimeout,
MaxIdleTimeout: l.md.maxIdleTimeout, MaxIdleTimeout: l.md.maxIdleTimeout,

View File

@ -66,7 +66,7 @@ func (l *wtListener) Init(md md.Metadata) (err error) {
H3: http3.Server{ H3: http3.Server{
Addr: l.options.Addr, Addr: l.options.Addr,
TLSConfig: l.options.TLSConfig, TLSConfig: l.options.TLSConfig,
QuicConfig: &quic.Config{ QUICConfig: &quic.Config{
KeepAlivePeriod: l.md.keepAlivePeriod, KeepAlivePeriod: l.md.keepAlivePeriod,
HandshakeIdleTimeout: l.md.handshakeTimeout, HandshakeIdleTimeout: l.md.handshakeTimeout,
MaxIdleTimeout: l.md.maxIdleTimeout, MaxIdleTimeout: l.md.maxIdleTimeout,

View File

@ -52,7 +52,12 @@ func (l *kcpListener) parseMetadata(md mdata.Metadata) (err error) {
l.md.config.KeepAlive = mdutil.GetInt(md, "kcp.keepalive") l.md.config.KeepAlive = mdutil.GetInt(md, "kcp.keepalive")
l.md.config.Interval = mdutil.GetInt(md, "kcp.interval") l.md.config.Interval = mdutil.GetInt(md, "kcp.interval")
l.md.config.MTU = mdutil.GetInt(md, "kcp.mtu") l.md.config.MTU = mdutil.GetInt(md, "kcp.mtu")
l.md.config.SmuxVer = mdutil.GetInt(md, "kcp.smuxVer") l.md.config.RcvWnd = mdutil.GetInt(md, "kcp.rcvwnd")
l.md.config.SndWnd = mdutil.GetInt(md, "kcp.sndwnd")
l.md.config.SmuxVer = mdutil.GetInt(md, "kcp.smuxver")
l.md.config.SmuxBuf = mdutil.GetInt(md, "kcp.smuxbuf")
l.md.config.StreamBuf = mdutil.GetInt(md, "kcp.streambuf")
l.md.config.NoComp = mdutil.GetBool(md, "kcp.nocomp")
l.md.backlog = mdutil.GetInt(md, backlog) l.md.backlog = mdutil.GetInt(md, backlog)
if l.md.backlog <= 0 { if l.md.backlog <= 0 {

View File

@ -0,0 +1,9 @@
package tcp
import (
"syscall"
)
func (l *redirectListener) control(network, address string, c syscall.RawConn) error {
return nil
}

View File

@ -1,4 +1,4 @@
//go:build !linux //go:build !linux && !darwin
package tcp package tcp

View File

@ -6,6 +6,7 @@ import (
mdata "github.com/go-gost/core/metadata" mdata "github.com/go-gost/core/metadata"
mdutil "github.com/go-gost/core/metadata/util" mdutil "github.com/go-gost/core/metadata/util"
ssh_util "github.com/go-gost/x/internal/util/ssh" ssh_util "github.com/go-gost/x/internal/util/ssh"
"github.com/mitchellh/go-homedir"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
) )
@ -29,6 +30,10 @@ func (l *sshListener) parseMetadata(md mdata.Metadata) (err error) {
) )
if key := mdutil.GetString(md, privateKeyFile); key != "" { if key := mdutil.GetString(md, privateKeyFile); key != "" {
key, err = homedir.Expand(key)
if err != nil {
return err
}
data, err := os.ReadFile(key) data, err := os.ReadFile(key)
if err != nil { if err != nil {
return err return err

View File

@ -1,11 +1,14 @@
package ssh package ssh
import ( import (
"fmt"
"os" "os"
mdata "github.com/go-gost/core/metadata" mdata "github.com/go-gost/core/metadata"
mdutil "github.com/go-gost/core/metadata/util" mdutil "github.com/go-gost/core/metadata/util"
ssh_util "github.com/go-gost/x/internal/util/ssh" ssh_util "github.com/go-gost/x/internal/util/ssh"
"github.com/mitchellh/go-homedir"
"github.com/zalando/go-keyring"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
) )
@ -29,12 +32,24 @@ func (l *sshdListener) parseMetadata(md mdata.Metadata) (err error) {
) )
if key := mdutil.GetString(md, privateKeyFile); key != "" { if key := mdutil.GetString(md, privateKeyFile); key != "" {
key, err = homedir.Expand(key)
if err != nil {
return err
}
data, err := os.ReadFile(key) data, err := os.ReadFile(key)
if err != nil { if err != nil {
return err return err
} }
pp := mdutil.GetString(md, passphrase) var pp string
if mdutil.GetBool(md, "passphraseFromKeyring") {
pp, err = keyring.Get(fmt.Sprintf("SSH %s", key), l.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 == "" { if pp == "" {
l.md.signer, err = ssh.ParsePrivateKey(data) l.md.signer, err = ssh.ParsePrivateKey(data)
} else { } else {

View File

@ -33,6 +33,7 @@ func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) observer.Obs
conn, err := plugin.NewGRPCConn(addr, &options) conn, err := plugin.NewGRPCConn(addr, &options)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
return nil
} }
p := &grpcPlugin{ p := &grpcPlugin{

View File

@ -6,6 +6,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
"strings"
"github.com/go-gost/core/logger" "github.com/go-gost/core/logger"
"github.com/go-gost/core/observer" "github.com/go-gost/core/observer"
@ -58,6 +59,9 @@ func NewHTTPPlugin(name string, url string, opts ...plugin.Option) observer.Obse
opt(&options) opt(&options)
} }
if !strings.HasPrefix(url, "http") {
url = "http://" + url
}
return &httpPlugin{ return &httpPlugin{
url: url, url: url,
client: plugin.NewHTTPClient(&options), client: plugin.NewHTTPClient(&options),

View File

@ -69,6 +69,10 @@ func (p *grpcPlugin) Record(ctx context.Context, b []byte, opts ...recorder.Reco
} }
func (p *grpcPlugin) Close() error { func (p *grpcPlugin) Close() error {
if p.conn == nil {
return nil
}
if closer, ok := p.conn.(io.Closer); ok { if closer, ok := p.conn.(io.Closer); ok {
return closer.Close() return closer.Close()
} }

View File

@ -37,3 +37,11 @@ func (w *bypassWrapper) Contains(ctx context.Context, network, addr string, opts
} }
return bp.Contains(ctx, network, addr, opts...) return bp.Contains(ctx, network, addr, opts...)
} }
func (p *bypassWrapper) IsWhitelist() bool {
bp := p.r.get(p.name)
if bp == nil {
return false
}
return bp.IsWhitelist()
}

View File

@ -70,6 +70,10 @@ func (p *grpcPlugin) Resolve(ctx context.Context, network, host string, opts ...
} }
func (p *grpcPlugin) Close() error { func (p *grpcPlugin) Close() error {
if p.conn == nil {
return nil
}
if closer, ok := p.conn.(io.Closer); ok { if closer, ok := p.conn.(io.Closer); ok {
return closer.Close() return closer.Close()
} }

View File

@ -63,6 +63,10 @@ func (p *grpcPlugin) GetRoute(ctx context.Context, dst net.IP, opts ...router.Op
} }
func (p *grpcPlugin) Close() error { func (p *grpcPlugin) Close() error {
if p.conn == nil {
return nil
}
if closer, ok := p.conn.(io.Closer); ok { if closer, ok := p.conn.(io.Closer); ok {
return closer.Close() return closer.Close()
} }

View File

@ -127,6 +127,10 @@ func (p *grpcPlugin) Get(ctx context.Context, name string) ([]*sd.Service, error
} }
func (p *grpcPlugin) Close() error { func (p *grpcPlugin) Close() error {
if p.conn == nil {
return nil
}
if closer, ok := p.conn.(io.Closer); ok { if closer, ok := p.conn.(io.Closer); ok {
return closer.Close() return closer.Close()
} }

View File

@ -24,15 +24,16 @@ import (
) )
type options struct { type options struct {
admission admission.Admission admission admission.Admission
recorders []recorder.RecorderObject recorders []recorder.RecorderObject
preUp []string preUp []string
postUp []string postUp []string
preDown []string preDown []string
postDown []string postDown []string
stats *stats.Stats stats *stats.Stats
observer observer.Observer observer observer.Observer
logger logger.Logger observePeriod time.Duration
logger logger.Logger
} }
type Option func(opts *options) type Option func(opts *options)
@ -85,6 +86,12 @@ func ObserverOption(observer observer.Observer) Option {
} }
} }
func ObservePeriodOption(period time.Duration) Option {
return func(opts *options) {
opts.observePeriod = period
}
}
func LoggerOption(logger logger.Logger) Option { func LoggerOption(logger logger.Logger) Option {
return func(opts *options) { return func(opts *options) {
opts.logger = logger opts.logger = logger
@ -129,6 +136,10 @@ func (s *defaultService) Addr() net.Addr {
func (s *defaultService) Serve() error { func (s *defaultService) Serve() error {
s.execCmds("post-up", s.options.postUp) s.execCmds("post-up", s.options.postUp)
s.setState(StateReady) s.setState(StateReady)
s.status.addEvent(Event{
Time: time.Now(),
Message: fmt.Sprintf("service %s is listening on %s", s.name, s.listener.Addr()),
})
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
@ -290,7 +301,12 @@ func (s *defaultService) observeStats(ctx context.Context) {
return return
} }
ticker := time.NewTicker(5 * time.Second) d := s.options.observePeriod
if d < time.Millisecond {
d = 5 * time.Second
}
ticker := time.NewTicker(d)
defer ticker.Stop() defer ticker.Stop()
for { for {

View File

@ -72,5 +72,8 @@ func (p *Status) addEvent(event Event) {
} }
func (p *Status) Stats() *stats.Stats { func (p *Status) Stats() *stats.Stats {
if p == nil {
return nil
}
return p.stats return p.stats
} }