add api & config

This commit is contained in:
ginuerzh 2022-03-17 22:54:57 +08:00
parent 2682c4d63b
commit 0f1f7790c5
20 changed files with 3456 additions and 18 deletions

15
api/api.go Normal file
View File

@ -0,0 +1,15 @@
package api
import (
"embed"
)
var (
//go:embed swagger.yaml
swaggerDoc embed.FS
)
type Response struct {
Code int `json:"code,omitempty"`
Msg string `json:"msg,omitempty"`
}

118
api/config.go Normal file
View File

@ -0,0 +1,118 @@
package api
import (
"bytes"
"fmt"
"net/http"
"os"
"github.com/gin-gonic/gin"
"github.com/go-gost/x/config"
)
// swagger:parameters getConfigRequest
type getConfigRequest struct {
// output format, one of yaml|json, default is json.
// in: query
Format string `form:"format" json:"format"`
}
// successful operation.
// swagger:response getConfigResponse
type getConfigResponse struct {
Config *config.Config
}
func getConfig(ctx *gin.Context) {
// swagger:route GET /config ConfigManagement getConfigRequest
//
// Get current config.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: getConfigResponse
var req getConfigRequest
ctx.ShouldBindQuery(&req)
var resp getConfigResponse
resp.Config = config.Global()
buf := &bytes.Buffer{}
switch req.Format {
case "yaml":
default:
req.Format = "json"
}
resp.Config.Write(buf, req.Format)
contentType := "application/json"
if req.Format == "yaml" {
contentType = "text/x-yaml"
}
ctx.Data(http.StatusOK, contentType, buf.Bytes())
}
// swagger:parameters saveConfigRequest
type saveConfigRequest struct {
// output format, one of yaml|json, default is yaml.
// in: query
Format string `form:"format" json:"format"`
}
// successful operation.
// swagger:response saveConfigResponse
type saveConfigResponse struct {
Data Response
}
func saveConfig(ctx *gin.Context) {
// swagger:route POST /config ConfigManagement saveConfigRequest
//
// Save current config to file (gost.yaml or gost.json).
//
// Security:
// basicAuth: []
//
// Responses:
// 200: saveConfigResponse
var req saveConfigRequest
ctx.ShouldBindQuery(&req)
file := "gost.yaml"
switch req.Format {
case "json":
file = "gost.json"
default:
req.Format = "yaml"
}
f, err := os.Create(file)
if err != nil {
writeError(ctx, &Error{
statusCode: http.StatusInternalServerError,
Code: 40005,
Msg: fmt.Sprintf("create file: %s", err.Error()),
})
return
}
defer f.Close()
if err := config.Global().Write(f, req.Format); err != nil {
writeError(ctx, &Error{
statusCode: http.StatusInternalServerError,
Code: 40006,
Msg: fmt.Sprintf("write: %s", err.Error()),
})
return
}
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}

166
api/config_admission.go Normal file
View File

@ -0,0 +1,166 @@
package api
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/go-gost/core/registry"
"github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing"
)
// swagger:parameters createAdmissionRequest
type createAdmissionRequest struct {
// in: body
Data config.AdmissionConfig `json:"data"`
}
// successful operation.
// swagger:response createAdmissionResponse
type createAdmissionResponse struct {
Data Response
}
func createAdmission(ctx *gin.Context) {
// swagger:route POST /config/admissions ConfigManagement createAdmissionRequest
//
// Create a new admission, the name of admission must be unique in admission list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: createAdmissionResponse
var req createAdmissionRequest
ctx.ShouldBindJSON(&req.Data)
if req.Data.Name == "" {
writeError(ctx, ErrInvalid)
return
}
v := parsing.ParseAdmission(&req.Data)
if err := registry.AdmissionRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
cfg.Admissions = append(cfg.Admissions, &req.Data)
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters updateAdmissionRequest
type updateAdmissionRequest struct {
// in: path
// required: true
Admission string `uri:"admission" json:"admission"`
// in: body
Data config.AdmissionConfig `json:"data"`
}
// successful operation.
// swagger:response updateAdmissionResponse
type updateAdmissionResponse struct {
Data Response
}
func updateAdmission(ctx *gin.Context) {
// swagger:route PUT /config/admissions/{admission} ConfigManagement updateAdmissionRequest
//
// Update admission by name, the admission must already exist.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: updateAdmissionResponse
var req updateAdmissionRequest
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.AdmissionRegistry().IsRegistered(req.Admission) {
writeError(ctx, ErrNotFound)
return
}
req.Data.Name = req.Admission
v := parsing.ParseAdmission(&req.Data)
registry.AdmissionRegistry().Unregister(req.Admission)
if err := registry.AdmissionRegistry().Register(req.Admission, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
for i := range cfg.Admissions {
if cfg.Admissions[i].Name == req.Admission {
cfg.Admissions[i] = &req.Data
break
}
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters deleteAdmissionRequest
type deleteAdmissionRequest struct {
// in: path
// required: true
Admission string `uri:"admission" json:"admission"`
}
// successful operation.
// swagger:response deleteAdmissionResponse
type deleteAdmissionResponse struct {
Data Response
}
func deleteAdmission(ctx *gin.Context) {
// swagger:route DELETE /config/admissions/{admission} ConfigManagement deleteAdmissionRequest
//
// Delete admission by name.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: deleteAdmissionResponse
var req deleteAdmissionRequest
ctx.ShouldBindUri(&req)
if !registry.AdmissionRegistry().IsRegistered(req.Admission) {
writeError(ctx, ErrNotFound)
return
}
registry.AdmissionRegistry().Unregister(req.Admission)
cfg := config.Global()
admissiones := cfg.Admissions
cfg.Admissions = nil
for _, s := range admissiones {
if s.Name == req.Admission {
continue
}
cfg.Admissions = append(cfg.Admissions, s)
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}

164
api/config_auther.go Normal file
View File

@ -0,0 +1,164 @@
package api
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/go-gost/core/registry"
"github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing"
)
// swagger:parameters createAutherRequest
type createAutherRequest struct {
// in: body
Data config.AutherConfig `json:"data"`
}
// successful operation.
// swagger:response createAutherResponse
type createAutherResponse struct {
Data Response
}
func createAuther(ctx *gin.Context) {
// swagger:route POST /config/authers ConfigManagement createAutherRequest
//
// Create a new auther, the name of the auther must be unique in auther list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: createAutherResponse
var req createAutherRequest
ctx.ShouldBindJSON(&req.Data)
if req.Data.Name == "" {
writeError(ctx, ErrInvalid)
return
}
v := parsing.ParseAuther(&req.Data)
if err := registry.AutherRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
cfg.Authers = append(cfg.Authers, &req.Data)
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters updateAutherRequest
type updateAutherRequest struct {
// in: path
// required: true
Auther string `uri:"auther" json:"auther"`
// in: body
Data config.AutherConfig `json:"data"`
}
// successful operation.
// swagger:response updateAutherResponse
type updateAutherResponse struct {
Data Response
}
func updateAuther(ctx *gin.Context) {
// swagger:route PUT /config/authers/{auther} ConfigManagement updateAutherRequest
//
// Update auther by name, the auther must already exist.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: updateAutherResponse
var req updateAutherRequest
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.AutherRegistry().IsRegistered(req.Auther) {
writeError(ctx, ErrNotFound)
return
}
req.Data.Name = req.Auther
v := parsing.ParseAuther(&req.Data)
registry.AutherRegistry().Unregister(req.Auther)
if err := registry.AutherRegistry().Register(req.Auther, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
for i := range cfg.Authers {
if cfg.Authers[i].Name == req.Auther {
cfg.Authers[i] = &req.Data
break
}
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters deleteAutherRequest
type deleteAutherRequest struct {
// in: path
// required: true
Auther string `uri:"auther" json:"auther"`
}
// successful operation.
// swagger:response deleteAutherResponse
type deleteAutherResponse struct {
Data Response
}
func deleteAuther(ctx *gin.Context) {
// swagger:route DELETE /config/authers/{auther} ConfigManagement deleteAutherRequest
//
// Delete auther by name.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: deleteAutherResponse
var req deleteAutherRequest
ctx.ShouldBindUri(&req)
if !registry.AutherRegistry().IsRegistered(req.Auther) {
writeError(ctx, ErrNotFound)
return
}
registry.AutherRegistry().Unregister(req.Auther)
cfg := config.Global()
authers := cfg.Authers
cfg.Authers = nil
for _, s := range authers {
if s.Name == req.Auther {
continue
}
cfg.Authers = append(cfg.Authers, s)
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}

166
api/config_bypass.go Normal file
View File

@ -0,0 +1,166 @@
package api
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/go-gost/core/registry"
"github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing"
)
// swagger:parameters createBypassRequest
type createBypassRequest struct {
// in: body
Data config.BypassConfig `json:"data"`
}
// successful operation.
// swagger:response createBypassResponse
type createBypassResponse struct {
Data Response
}
func createBypass(ctx *gin.Context) {
// swagger:route POST /config/bypasses ConfigManagement createBypassRequest
//
// Create a new bypass, the name of bypass must be unique in bypass list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: createBypassResponse
var req createBypassRequest
ctx.ShouldBindJSON(&req.Data)
if req.Data.Name == "" {
writeError(ctx, ErrInvalid)
return
}
v := parsing.ParseBypass(&req.Data)
if err := registry.BypassRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
cfg.Bypasses = append(cfg.Bypasses, &req.Data)
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters updateBypassRequest
type updateBypassRequest struct {
// in: path
// required: true
Bypass string `uri:"bypass" json:"bypass"`
// in: body
Data config.BypassConfig `json:"data"`
}
// successful operation.
// swagger:response updateBypassResponse
type updateBypassResponse struct {
Data Response
}
func updateBypass(ctx *gin.Context) {
// swagger:route PUT /config/bypasses/{bypass} ConfigManagement updateBypassRequest
//
// Update bypass by name, the bypass must already exist.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: updateBypassResponse
var req updateBypassRequest
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.BypassRegistry().IsRegistered(req.Bypass) {
writeError(ctx, ErrNotFound)
return
}
req.Data.Name = req.Bypass
v := parsing.ParseBypass(&req.Data)
registry.BypassRegistry().Unregister(req.Bypass)
if err := registry.BypassRegistry().Register(req.Bypass, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
for i := range cfg.Bypasses {
if cfg.Bypasses[i].Name == req.Bypass {
cfg.Bypasses[i] = &req.Data
break
}
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters deleteBypassRequest
type deleteBypassRequest struct {
// in: path
// required: true
Bypass string `uri:"bypass" json:"bypass"`
}
// successful operation.
// swagger:response deleteBypassResponse
type deleteBypassResponse struct {
Data Response
}
func deleteBypass(ctx *gin.Context) {
// swagger:route DELETE /config/bypasses/{bypass} ConfigManagement deleteBypassRequest
//
// Delete bypass by name.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: deleteBypassResponse
var req deleteBypassRequest
ctx.ShouldBindUri(&req)
if !registry.BypassRegistry().IsRegistered(req.Bypass) {
writeError(ctx, ErrNotFound)
return
}
registry.BypassRegistry().Unregister(req.Bypass)
cfg := config.Global()
bypasses := cfg.Bypasses
cfg.Bypasses = nil
for _, s := range bypasses {
if s.Name == req.Bypass {
continue
}
cfg.Bypasses = append(cfg.Bypasses, s)
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}

175
api/config_chain.go Normal file
View File

@ -0,0 +1,175 @@
package api
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/go-gost/core/registry"
"github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing"
)
// swagger:parameters createChainRequest
type createChainRequest struct {
// in: body
Data config.ChainConfig `json:"data"`
}
// successful operation.
// swagger:response createChainResponse
type createChainResponse struct {
Data Response
}
func createChain(ctx *gin.Context) {
// swagger:route POST /config/chains ConfigManagement createChainRequest
//
// Create a new chain, the name of chain must be unique in chain list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: createChainResponse
var req createChainRequest
ctx.ShouldBindJSON(&req.Data)
if req.Data.Name == "" {
writeError(ctx, ErrInvalid)
return
}
v, err := parsing.ParseChain(&req.Data)
if err != nil {
writeError(ctx, ErrCreate)
return
}
if err := registry.ChainRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
cfg.Chains = append(cfg.Chains, &req.Data)
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters updateChainRequest
type updateChainRequest struct {
// in: path
// required: true
// chain name
Chain string `uri:"chain" json:"chain"`
// in: body
Data config.ChainConfig `json:"data"`
}
// successful operation.
// swagger:response updateChainResponse
type updateChainResponse struct {
Data Response
}
func updateChain(ctx *gin.Context) {
// swagger:route PUT /config/chains/{chain} ConfigManagement updateChainRequest
//
// Update chain by name, the chain must already exist.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: updateChainResponse
var req updateChainRequest
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.ChainRegistry().IsRegistered(req.Chain) {
writeError(ctx, ErrNotFound)
return
}
req.Data.Name = req.Chain
v, err := parsing.ParseChain(&req.Data)
if err != nil {
writeError(ctx, ErrCreate)
return
}
registry.ChainRegistry().Unregister(req.Chain)
if err := registry.ChainRegistry().Register(req.Chain, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
for i := range cfg.Chains {
if cfg.Chains[i].Name == req.Chain {
cfg.Chains[i] = &req.Data
break
}
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters deleteChainRequest
type deleteChainRequest struct {
// in: path
// required: true
Chain string `uri:"chain" json:"chain"`
}
// successful operation.
// swagger:response deleteChainResponse
type deleteChainResponse struct {
Data Response
}
func deleteChain(ctx *gin.Context) {
// swagger:route DELETE /config/chains/{chain} ConfigManagement deleteChainRequest
//
// Delete chain by name.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: deleteChainResponse
var req deleteChainRequest
ctx.ShouldBindUri(&req)
if !registry.ChainRegistry().IsRegistered(req.Chain) {
writeError(ctx, ErrNotFound)
return
}
registry.ChainRegistry().Unregister(req.Chain)
cfg := config.Global()
chains := cfg.Chains
cfg.Chains = nil
for _, s := range chains {
if s.Name == req.Chain {
continue
}
cfg.Chains = append(cfg.Chains, s)
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}

166
api/config_hosts.go Normal file
View File

@ -0,0 +1,166 @@
package api
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/go-gost/core/registry"
"github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing"
)
// swagger:parameters createHostsRequest
type createHostsRequest struct {
// in: body
Data config.HostsConfig `json:"data"`
}
// successful operation.
// swagger:response createHostsResponse
type createHostsesponse struct {
Data Response
}
func createHosts(ctx *gin.Context) {
// swagger:route POST /config/hosts ConfigManagement createHostsRequest
//
// Create a new hosts, the name of the hosts must be unique in hosts list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: createHostsResponse
var req createHostsRequest
ctx.ShouldBindJSON(&req.Data)
if req.Data.Name == "" {
writeError(ctx, ErrInvalid)
return
}
v := parsing.ParseHosts(&req.Data)
if err := registry.HostsRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
cfg.Hosts = append(cfg.Hosts, &req.Data)
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters updateHostsRequest
type updateHostsRequest struct {
// in: path
// required: true
Hosts string `uri:"hosts" json:"hosts"`
// in: body
Data config.HostsConfig `json:"data"`
}
// successful operation.
// swagger:response updateHostsResponse
type updateHostsResponse struct {
Data Response
}
func updateHosts(ctx *gin.Context) {
// swagger:route PUT /config/hosts/{hosts} ConfigManagement updateHostsRequest
//
// Update hosts by name, the hosts must already exist.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: updateHostsResponse
var req updateHostsRequest
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.HostsRegistry().IsRegistered(req.Hosts) {
writeError(ctx, ErrNotFound)
return
}
req.Data.Name = req.Hosts
v := parsing.ParseHosts(&req.Data)
registry.HostsRegistry().Unregister(req.Hosts)
if err := registry.HostsRegistry().Register(req.Hosts, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
for i := range cfg.Hosts {
if cfg.Hosts[i].Name == req.Hosts {
cfg.Hosts[i] = &req.Data
break
}
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters deleteHostsRequest
type deleteHostsRequest struct {
// in: path
// required: true
Hosts string `uri:"hosts" json:"hosts"`
}
// successful operation.
// swagger:response deleteHostsResponse
type deleteHostsResponse struct {
Data Response
}
func deleteHosts(ctx *gin.Context) {
// swagger:route DELETE /config/hosts/{hosts} ConfigManagement deleteHostsRequest
//
// Delete hosts by name.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: deleteHostsResponse
var req deleteHostsRequest
ctx.ShouldBindUri(&req)
if !registry.HostsRegistry().IsRegistered(req.Hosts) {
writeError(ctx, ErrNotFound)
return
}
registry.HostsRegistry().Unregister(req.Hosts)
cfg := config.Global()
hosts := cfg.Hosts
cfg.Hosts = nil
for _, s := range hosts {
if s.Name == req.Hosts {
continue
}
cfg.Hosts = append(cfg.Hosts, s)
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}

174
api/config_resolver.go Normal file
View File

@ -0,0 +1,174 @@
package api
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/go-gost/core/registry"
"github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing"
)
// swagger:parameters createResolverRequest
type createResolverRequest struct {
// in: body
Data config.ResolverConfig `json:"data"`
}
// successful operation.
// swagger:response createResolverResponse
type createResolverResponse struct {
Data Response
}
func createResolver(ctx *gin.Context) {
// swagger:route POST /config/resolvers ConfigManagement createResolverRequest
//
// Create a new resolver, the name of the resolver must be unique in resolver list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: createResolverResponse
var req createResolverRequest
ctx.ShouldBindJSON(&req.Data)
if req.Data.Name == "" {
writeError(ctx, ErrInvalid)
return
}
v, err := parsing.ParseResolver(&req.Data)
if err != nil {
writeError(ctx, ErrCreate)
return
}
if err := registry.ResolverRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
cfg.Resolvers = append(cfg.Resolvers, &req.Data)
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters updateResolverRequest
type updateResolverRequest struct {
// in: path
// required: true
Resolver string `uri:"resolver" json:"resolver"`
// in: body
Data config.ResolverConfig `json:"data"`
}
// successful operation.
// swagger:response updateResolverResponse
type updateResolverResponse struct {
Data Response
}
func updateResolver(ctx *gin.Context) {
// swagger:route PUT /config/resolvers/{resolver} ConfigManagement updateResolverRequest
//
// Update resolver by name, the resolver must already exist.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: updateResolverResponse
var req updateResolverRequest
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.ResolverRegistry().IsRegistered(req.Resolver) {
writeError(ctx, ErrNotFound)
return
}
req.Data.Name = req.Resolver
v, err := parsing.ParseResolver(&req.Data)
if err != nil {
writeError(ctx, ErrCreate)
return
}
registry.ResolverRegistry().Unregister(req.Resolver)
if err := registry.ResolverRegistry().Register(req.Resolver, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
for i := range cfg.Resolvers {
if cfg.Resolvers[i].Name == req.Resolver {
cfg.Resolvers[i] = &req.Data
break
}
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters deleteResolverRequest
type deleteResolverRequest struct {
// in: path
// required: true
Resolver string `uri:"resolver" json:"resolver"`
}
// successful operation.
// swagger:response deleteResolverResponse
type deleteResolverResponse struct {
Data Response
}
func deleteResolver(ctx *gin.Context) {
// swagger:route DELETE /config/resolvers/{resolver} ConfigManagement deleteResolverRequest
//
// Delete resolver by name.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: deleteResolverResponse
var req deleteResolverRequest
ctx.ShouldBindUri(&req)
if !registry.ResolverRegistry().IsRegistered(req.Resolver) {
writeError(ctx, ErrNotFound)
return
}
registry.ResolverRegistry().Unregister(req.Resolver)
cfg := config.Global()
resolvers := cfg.Resolvers
cfg.Resolvers = nil
for _, s := range resolvers {
if s.Name == req.Resolver {
continue
}
cfg.Resolvers = append(cfg.Resolvers, s)
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}

190
api/config_service.go Normal file
View File

@ -0,0 +1,190 @@
package api
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/go-gost/core/registry"
"github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing"
)
// swagger:parameters createServiceRequest
type createServiceRequest struct {
// in: body
Data config.ServiceConfig `json:"data"`
}
// successful operation.
// swagger:response createServiceResponse
type createServiceResponse struct {
Data Response
}
func createService(ctx *gin.Context) {
// swagger:route POST /config/services ConfigManagement createServiceRequest
//
// Create a new service, the name of the service must be unique in service list.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: createServiceResponse
var req createServiceRequest
ctx.ShouldBindJSON(&req.Data)
if req.Data.Name == "" {
writeError(ctx, ErrInvalid)
return
}
if registry.ServiceRegistry().IsRegistered(req.Data.Name) {
writeError(ctx, ErrDup)
return
}
svc, err := parsing.ParseService(&req.Data)
if err != nil {
writeError(ctx, ErrCreate)
return
}
if err := registry.ServiceRegistry().Register(req.Data.Name, svc); err != nil {
svc.Close()
writeError(ctx, ErrDup)
return
}
go svc.Serve()
cfg := config.Global()
cfg.Services = append(cfg.Services, &req.Data)
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters updateServiceRequest
type updateServiceRequest struct {
// in: path
// required: true
Service string `uri:"service" json:"service"`
// in: body
Data config.ServiceConfig `json:"data"`
}
// successful operation.
// swagger:response updateServiceResponse
type updateServiceResponse struct {
Data Response
}
func updateService(ctx *gin.Context) {
// swagger:route PUT /config/services/{service} ConfigManagement updateServiceRequest
//
// Update service by name, the service must already exist.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: updateServiceResponse
var req updateServiceRequest
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
old := registry.ServiceRegistry().Get(req.Service)
if old == nil {
writeError(ctx, ErrNotFound)
return
}
old.Close()
req.Data.Name = req.Service
svc, err := parsing.ParseService(&req.Data)
if err != nil {
writeError(ctx, ErrCreate)
return
}
registry.ServiceRegistry().Unregister(req.Service)
if err := registry.ServiceRegistry().Register(req.Service, svc); err != nil {
svc.Close()
writeError(ctx, ErrDup)
return
}
go svc.Serve()
cfg := config.Global()
for i := range cfg.Services {
if cfg.Services[i].Name == req.Service {
cfg.Services[i] = &req.Data
break
}
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters deleteServiceRequest
type deleteServiceRequest struct {
// in: path
// required: true
Service string `uri:"service" json:"service"`
}
// successful operation.
// swagger:response deleteServiceResponse
type deleteServiceResponse struct {
Data Response
}
func deleteService(ctx *gin.Context) {
// swagger:route DELETE /config/services/{service} ConfigManagement deleteServiceRequest
//
// Delete service by name.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: deleteServiceResponse
var req deleteServiceRequest
ctx.ShouldBindUri(&req)
svc := registry.ServiceRegistry().Get(req.Service)
if svc == nil {
writeError(ctx, ErrNotFound)
return
}
registry.ServiceRegistry().Unregister(req.Service)
svc.Close()
cfg := config.Global()
services := cfg.Services
cfg.Services = nil
for _, s := range services {
if s.Name == req.Service {
continue
}
cfg.Services = append(cfg.Services, s)
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}

18
api/doc.go Normal file
View File

@ -0,0 +1,18 @@
// Documentation of Web API.
//
// Schemes: https, http
// BasePath: /
// Version: 1.0.0
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// SecurityDefinitions:
// basicAuth:
// type: basic
//
// swagger:meta
package api

45
api/error.go Normal file
View File

@ -0,0 +1,45 @@
package api
import (
"encoding/json"
"net/http"
"github.com/gin-gonic/gin"
)
var (
ErrInvalid = &Error{statusCode: http.StatusBadRequest, Code: 40001, Msg: "object invalid"}
ErrDup = &Error{statusCode: http.StatusBadRequest, Code: 40002, Msg: "object duplicated"}
ErrCreate = &Error{statusCode: http.StatusConflict, Code: 40003, Msg: "object creation failed"}
ErrNotFound = &Error{statusCode: http.StatusBadRequest, Code: 40004, Msg: "object not found"}
ErrSave = &Error{statusCode: http.StatusInternalServerError, Code: 40005, Msg: "save config failed"}
)
// Error is an api error.
type Error struct {
statusCode int
Code int `json:"code"`
Msg string `json:"msg"`
}
func (e *Error) Error() string {
b, _ := json.Marshal(e)
return string(b)
}
func writeError(c *gin.Context, err error) {
// c.Set(HTTPResponseTag, err)
c.JSON(getStatusCode(err), err)
}
func getStatusCode(err error) int {
if err == nil {
return http.StatusOK
}
if e, ok := err.(*Error); ok {
if e.statusCode >= http.StatusOK && e.statusCode < 600 {
return e.statusCode
}
}
return http.StatusInternalServerError
}

42
api/middleware.go Normal file
View File

@ -0,0 +1,42 @@
package api
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/go-gost/core/auth"
"github.com/go-gost/core/logger"
)
func mwLogger() gin.HandlerFunc {
return func(ctx *gin.Context) {
// start time
startTime := time.Now()
// Processing request
ctx.Next()
duration := time.Since(startTime)
logger.Default().WithFields(map[string]any{
"kind": "api",
"method": ctx.Request.Method,
"uri": ctx.Request.RequestURI,
"code": ctx.Writer.Status(),
"client": ctx.ClientIP(),
"duration": duration,
}).Infof("| %3d | %13v | %15s | %-7s %s",
ctx.Writer.Status(), duration, ctx.ClientIP(), ctx.Request.Method, ctx.Request.RequestURI)
}
}
func mwBasicAuth(auther auth.Authenticator) gin.HandlerFunc {
return func(c *gin.Context) {
if auther == nil {
return
}
u, p, _ := c.Request.BasicAuth()
if !auther.Authenticate(u, p) {
c.AbortWithStatus(http.StatusUnauthorized)
}
}
}

132
api/service.go Normal file
View File

@ -0,0 +1,132 @@
package api
import (
"net"
"net/http"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"github.com/go-gost/core/auth"
"github.com/go-gost/core/service"
)
type options struct {
accessLog bool
pathPrefix string
auther auth.Authenticator
}
type Option func(*options)
func PathPrefixOption(pathPrefix string) Option {
return func(o *options) {
o.pathPrefix = pathPrefix
}
}
func AccessLogOption(enable bool) Option {
return func(o *options) {
o.accessLog = enable
}
}
func AutherOption(auther auth.Authenticator) Option {
return func(o *options) {
o.auther = auther
}
}
type server struct {
s *http.Server
ln net.Listener
}
func NewService(addr string, opts ...Option) (service.Service, error) {
ln, err := net.Listen("tcp", addr)
if err != nil {
return nil, err
}
var options options
for _, opt := range opts {
opt(&options)
}
gin.SetMode(gin.ReleaseMode)
r := gin.New()
r.Use(
cors.New((cors.Config{
AllowAllOrigins: true,
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"*"},
})),
gin.Recovery(),
)
if options.accessLog {
r.Use(mwLogger())
}
router := r.Group("")
if options.pathPrefix != "" {
router = router.Group(options.pathPrefix)
}
router.StaticFS("/docs", http.FS(swaggerDoc))
config := router.Group("/config")
config.Use(mwBasicAuth(options.auther))
registerConfig(config)
return &server{
s: &http.Server{
Handler: r,
},
ln: ln,
}, nil
}
func (s *server) Serve() error {
return s.s.Serve(s.ln)
}
func (s *server) Addr() net.Addr {
return s.ln.Addr()
}
func (s *server) Close() error {
return s.s.Close()
}
func registerConfig(config *gin.RouterGroup) {
config.GET("", getConfig)
config.POST("", saveConfig)
config.POST("/services", createService)
config.PUT("/services/:service", updateService)
config.DELETE("/services/:service", deleteService)
config.POST("/chains", createChain)
config.PUT("/chains/:chain", updateChain)
config.DELETE("/chains/:chain", deleteChain)
config.POST("/authers", createAuther)
config.PUT("/authers/:auther", updateAuther)
config.DELETE("/authers/:auther", deleteAuther)
config.POST("/admissions", createAdmission)
config.PUT("/admissions/:admission", updateAdmission)
config.DELETE("/admissions/:admission", deleteAdmission)
config.POST("/bypasses", createBypass)
config.PUT("/bypasses/:bypass", updateBypass)
config.DELETE("/bypasses/:bypass", deleteBypass)
config.POST("/resolvers", createResolver)
config.PUT("/resolvers/:resolver", updateResolver)
config.DELETE("/resolvers/:resolver", deleteResolver)
config.POST("/hosts", createHosts)
config.PUT("/hosts/:hosts", updateHosts)
config.DELETE("/hosts/:hosts", deleteHosts)
}

1068
api/swagger.yaml Normal file

File diff suppressed because it is too large Load Diff

273
config/config.go Normal file
View File

@ -0,0 +1,273 @@
package config
import (
"encoding/json"
"io"
"sync"
"time"
"github.com/spf13/viper"
"gopkg.in/yaml.v2"
)
var (
v = viper.GetViper()
)
func init() {
v.SetConfigName("gost")
v.AddConfigPath("/etc/gost/")
v.AddConfigPath("$HOME/.gost/")
v.AddConfigPath(".")
}
var (
global = &Config{}
globalMux sync.RWMutex
)
func Global() *Config {
globalMux.RLock()
defer globalMux.RUnlock()
cfg := &Config{}
*cfg = *global
return cfg
}
func SetGlobal(c *Config) {
globalMux.Lock()
defer globalMux.Unlock()
global = c
}
type LogConfig struct {
Output string `yaml:",omitempty" json:"output,omitempty"`
Level string `yaml:",omitempty" json:"level,omitempty"`
Format string `yaml:",omitempty" json:"format,omitempty"`
}
type ProfilingConfig struct {
Addr string `json:"addr"`
}
type APIConfig struct {
Addr string `json:"addr"`
PathPrefix string `yaml:"pathPrefix,omitempty" json:"pathPrefix,omitempty"`
AccessLog bool `yaml:"accesslog,omitempty" json:"accesslog,omitempty"`
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
Auther string `yaml:",omitempty" json:"auther,omitempty"`
}
type MetricsConfig struct {
Addr string `json:"addr"`
Path string `json:"path"`
}
type TLSConfig struct {
CertFile string `yaml:"certFile,omitempty" json:"certFile,omitempty"`
KeyFile string `yaml:"keyFile,omitempty" json:"keyFile,omitempty"`
CAFile string `yaml:"caFile,omitempty" json:"caFile,omitempty"`
Secure bool `yaml:",omitempty" json:"secure,omitempty"`
ServerName string `yaml:"serverName,omitempty" json:"serverName,omitempty"`
}
type AutherConfig struct {
Name string `json:"name"`
// inline, file, redis, etc.
Type string `yaml:",omitempty" json:"type,omitempty"`
Auths []*AuthConfig `yaml:",omitempty" json:"auths"`
// File string `yaml:",omitempty" json:"file"`
}
type AuthConfig struct {
Username string `json:"username"`
Password string `yaml:",omitempty" json:"password,omitempty"`
}
type SelectorConfig struct {
Strategy string `json:"strategy"`
MaxFails int `yaml:"maxFails" json:"maxFails"`
FailTimeout time.Duration `yaml:"failTimeout" json:"failTimeout"`
}
type AdmissionConfig struct {
Name string `json:"name"`
// inline, file, etc.
Type string `yaml:",omitempty" json:"type,omitempty"`
Reverse bool `yaml:",omitempty" json:"reverse,omitempty"`
Matchers []string `json:"matchers"`
}
type BypassConfig struct {
Name string `json:"name"`
// inline, file, etc.
Type string `yaml:",omitempty" json:"type,omitempty"`
Reverse bool `yaml:",omitempty" json:"reverse,omitempty"`
Matchers []string `json:"matchers"`
}
type NameserverConfig struct {
Addr string `json:"addr"`
Chain string `yaml:",omitempty" json:"chain,omitempty"`
Prefer string `yaml:",omitempty" json:"prefer,omitempty"`
ClientIP string `yaml:"clientIP,omitempty" json:"clientIP,omitempty"`
Hostname string `yaml:",omitempty" json:"hostname,omitempty"`
TTL time.Duration `yaml:",omitempty" json:"ttl,omitempty"`
Timeout time.Duration `yaml:",omitempty" json:"timeout,omitempty"`
}
type ResolverConfig struct {
Name string `json:"name"`
// inline, file, etc.
Type string `yaml:",omitempty" json:"type,omitempty"`
Nameservers []*NameserverConfig `json:"nameservers"`
}
type HostMappingConfig struct {
IP string `json:"ip"`
Hostname string `json:"hostname"`
Aliases []string `yaml:",omitempty" json:"aliases,omitempty"`
}
type HostsConfig struct {
Name string `json:"name"`
// inline, file, etc.
Type string `yaml:",omitempty" json:"type,omitempty"`
Mappings []*HostMappingConfig `json:"mappings"`
}
type ListenerConfig struct {
Type string `json:"type"`
Chain string `yaml:",omitempty" json:"chain,omitempty"`
Auther string `yaml:",omitempty" json:"auther,omitempty"`
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
}
type HandlerConfig struct {
Type string `json:"type"`
Retries int `yaml:",omitempty" json:"retries,omitempty"`
Chain string `yaml:",omitempty" json:"chain,omitempty"`
Auther string `yaml:",omitempty" json:"auther,omitempty"`
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
}
type ForwarderConfig struct {
Targets []string `json:"targets"`
Selector *SelectorConfig `yaml:",omitempty" json:"selector,omitempty"`
}
type DialerConfig struct {
Type string `json:"type"`
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
}
type ConnectorConfig struct {
Type string `json:"type"`
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
}
type ServiceConfig struct {
Name string `json:"name"`
Addr string `yaml:",omitempty" json:"addr,omitempty"`
Interface string `yaml:",omitempty" json:"interface,omitempty"`
Admission string `yaml:",omitempty" json:"admission,omitempty"`
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
Handler *HandlerConfig `yaml:",omitempty" json:"handler,omitempty"`
Listener *ListenerConfig `yaml:",omitempty" json:"listener,omitempty"`
Forwarder *ForwarderConfig `yaml:",omitempty" json:"forwarder,omitempty"`
}
type ChainConfig struct {
Name string `json:"name"`
Selector *SelectorConfig `yaml:",omitempty" json:"selector,omitempty"`
Hops []*HopConfig `json:"hops"`
}
type HopConfig struct {
Name string `json:"name"`
Interface string `yaml:",omitempty" json:"interface,omitempty"`
Selector *SelectorConfig `yaml:",omitempty" json:"selector,omitempty"`
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
Nodes []*NodeConfig `json:"nodes"`
}
type NodeConfig struct {
Name string `json:"name"`
Addr string `yaml:",omitempty" json:"addr,omitempty"`
Interface string `yaml:",omitempty" json:"interface,omitempty"`
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
Connector *ConnectorConfig `yaml:",omitempty" json:"connector,omitempty"`
Dialer *DialerConfig `yaml:",omitempty" json:"dialer,omitempty"`
}
type Config struct {
Services []*ServiceConfig `json:"services"`
Chains []*ChainConfig `yaml:",omitempty" json:"chains,omitempty"`
Authers []*AutherConfig `yaml:",omitempty" json:"authers,omitempty"`
Admissions []*AdmissionConfig `yaml:",omitempty" json:"admissions,omitempty"`
Bypasses []*BypassConfig `yaml:",omitempty" json:"bypasses,omitempty"`
Resolvers []*ResolverConfig `yaml:",omitempty" json:"resolvers,omitempty"`
Hosts []*HostsConfig `yaml:",omitempty" json:"hosts,omitempty"`
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
Log *LogConfig `yaml:",omitempty" json:"log,omitempty"`
Profiling *ProfilingConfig `yaml:",omitempty" json:"profiling,omitempty"`
API *APIConfig `yaml:",omitempty" json:"api,omitempty"`
Metrics *MetricsConfig `yaml:",omitempty" json:"metrics,omitempty"`
}
func (c *Config) Load() error {
if err := v.ReadInConfig(); err != nil {
return err
}
return v.Unmarshal(c)
}
func (c *Config) Read(r io.Reader) error {
if err := v.ReadConfig(r); err != nil {
return err
}
return v.Unmarshal(c)
}
func (c *Config) ReadFile(file string) error {
v.SetConfigFile(file)
if err := v.ReadInConfig(); err != nil {
return err
}
return v.Unmarshal(c)
}
func (c *Config) Write(w io.Writer, format string) error {
switch format {
case "json":
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
enc.Encode(c)
return nil
case "yaml":
fallthrough
default:
enc := yaml.NewEncoder(w)
defer enc.Close()
return enc.Encode(c)
}
}

136
config/parsing/chain.go Normal file
View File

@ -0,0 +1,136 @@
package parsing
import (
"github.com/go-gost/core/chain"
tls_util "github.com/go-gost/core/common/util/tls"
"github.com/go-gost/core/connector"
"github.com/go-gost/core/dialer"
"github.com/go-gost/core/logger"
"github.com/go-gost/core/metadata"
"github.com/go-gost/core/registry"
"github.com/go-gost/x/config"
)
func ParseChain(cfg *config.ChainConfig) (chain.Chainer, error) {
if cfg == nil {
return nil, nil
}
chainLogger := logger.Default().WithFields(map[string]any{
"kind": "chain",
"chain": cfg.Name,
})
c := chain.NewChain(cfg.Name)
selector := parseSelector(cfg.Selector)
for _, hop := range cfg.Hops {
group := &chain.NodeGroup{}
for _, v := range hop.Nodes {
nodeLogger := chainLogger.WithFields(map[string]any{
"kind": "node",
"connector": v.Connector.Type,
"dialer": v.Dialer.Type,
"hop": hop.Name,
"node": v.Name,
})
connectorLogger := nodeLogger.WithFields(map[string]any{
"kind": "connector",
})
tlsCfg := v.Connector.TLS
if tlsCfg == nil {
tlsCfg = &config.TLSConfig{}
}
tlsConfig, err := tls_util.LoadClientConfig(
tlsCfg.CertFile, tlsCfg.KeyFile, tlsCfg.CAFile,
tlsCfg.Secure, tlsCfg.ServerName)
if err != nil {
chainLogger.Error(err)
return nil, err
}
cr := registry.ConnectorRegistry().Get(v.Connector.Type)(
connector.AuthOption(parseAuth(v.Connector.Auth)),
connector.TLSConfigOption(tlsConfig),
connector.LoggerOption(connectorLogger),
)
if v.Connector.Metadata == nil {
v.Connector.Metadata = make(map[string]any)
}
if err := cr.Init(metadata.MapMetadata(v.Connector.Metadata)); err != nil {
connectorLogger.Error("init: ", err)
return nil, err
}
dialerLogger := nodeLogger.WithFields(map[string]any{
"kind": "dialer",
})
tlsCfg = v.Dialer.TLS
if tlsCfg == nil {
tlsCfg = &config.TLSConfig{}
}
tlsConfig, err = tls_util.LoadClientConfig(
tlsCfg.CertFile, tlsCfg.KeyFile, tlsCfg.CAFile,
tlsCfg.Secure, tlsCfg.ServerName)
if err != nil {
chainLogger.Error(err)
return nil, err
}
d := registry.DialerRegistry().Get(v.Dialer.Type)(
dialer.AuthOption(parseAuth(v.Dialer.Auth)),
dialer.TLSConfigOption(tlsConfig),
dialer.LoggerOption(dialerLogger),
)
if v.Dialer.Metadata == nil {
v.Dialer.Metadata = make(map[string]any)
}
if err := d.Init(metadata.MapMetadata(v.Dialer.Metadata)); err != nil {
dialerLogger.Error("init: ", err)
return nil, err
}
if v.Bypass == "" {
v.Bypass = hop.Bypass
}
if v.Resolver == "" {
v.Resolver = hop.Resolver
}
if v.Hosts == "" {
v.Hosts = hop.Hosts
}
if v.Interface == "" {
v.Interface = hop.Interface
}
tr := (&chain.Transport{}).
WithConnector(cr).
WithDialer(d).
WithAddr(v.Addr).
WithInterface(v.Interface)
node := &chain.Node{
Name: v.Name,
Addr: v.Addr,
Bypass: registry.BypassRegistry().Get(v.Bypass),
Resolver: registry.ResolverRegistry().Get(v.Resolver),
Hosts: registry.HostsRegistry().Get(v.Hosts),
Marker: &chain.FailMarker{},
Transport: tr,
}
group.AddNode(node)
}
sel := selector
if s := parseSelector(hop.Selector); s != nil {
sel = s
}
group.WithSelector(sel)
c.AddNodeGroup(group)
}
return c, nil
}

161
config/parsing/parse.go Normal file
View File

@ -0,0 +1,161 @@
package parsing
import (
"net"
"net/url"
"github.com/go-gost/core/admission"
"github.com/go-gost/core/auth"
"github.com/go-gost/core/bypass"
"github.com/go-gost/core/chain"
hostspkg "github.com/go-gost/core/hosts"
"github.com/go-gost/core/logger"
"github.com/go-gost/core/registry"
"github.com/go-gost/core/resolver"
resolver_impl "github.com/go-gost/core/resolver/impl"
"github.com/go-gost/x/config"
)
func ParseAuther(cfg *config.AutherConfig) auth.Authenticator {
if cfg == nil {
return nil
}
m := make(map[string]string)
for _, user := range cfg.Auths {
if user.Username == "" {
continue
}
m[user.Username] = user.Password
}
if len(m) == 0 {
return nil
}
return auth.NewAuthenticator(m)
}
func ParseAutherFromAuth(au *config.AuthConfig) auth.Authenticator {
if au == nil || au.Username == "" {
return nil
}
return auth.NewAuthenticator(map[string]string{
au.Username: au.Password,
})
}
func parseAuth(cfg *config.AuthConfig) *url.Userinfo {
if cfg == nil || cfg.Username == "" {
return nil
}
if cfg.Password == "" {
return url.User(cfg.Username)
}
return url.UserPassword(cfg.Username, cfg.Password)
}
func parseSelector(cfg *config.SelectorConfig) chain.Selector {
if cfg == nil {
return nil
}
var strategy chain.Strategy
switch cfg.Strategy {
case "round", "rr":
strategy = chain.RoundRobinStrategy()
case "random", "rand":
strategy = chain.RandomStrategy()
case "fifo", "ha":
strategy = chain.FIFOStrategy()
default:
strategy = chain.RoundRobinStrategy()
}
return chain.NewSelector(
strategy,
chain.InvalidFilter(),
chain.FailFilter(cfg.MaxFails, cfg.FailTimeout),
)
}
func ParseAdmission(cfg *config.AdmissionConfig) admission.Admission {
if cfg == nil {
return nil
}
return admission.NewAdmissionPatterns(
cfg.Reverse,
cfg.Matchers,
admission.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "admission",
"admission": cfg.Name,
})),
)
}
func ParseBypass(cfg *config.BypassConfig) bypass.Bypass {
if cfg == nil {
return nil
}
return bypass.NewBypassPatterns(
cfg.Reverse,
cfg.Matchers,
bypass.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "bypass",
"bypass": cfg.Name,
})),
)
}
func ParseResolver(cfg *config.ResolverConfig) (resolver.Resolver, error) {
if cfg == nil {
return nil, nil
}
var nameservers []resolver_impl.NameServer
for _, server := range cfg.Nameservers {
nameservers = append(nameservers, resolver_impl.NameServer{
Addr: server.Addr,
Chain: registry.ChainRegistry().Get(server.Chain),
TTL: server.TTL,
Timeout: server.Timeout,
ClientIP: net.ParseIP(server.ClientIP),
Prefer: server.Prefer,
Hostname: server.Hostname,
})
}
return resolver_impl.NewResolver(
nameservers,
resolver_impl.LoggerResolverOption(
logger.Default().WithFields(map[string]any{
"kind": "resolver",
"resolver": cfg.Name,
}),
),
)
}
func ParseHosts(cfg *config.HostsConfig) hostspkg.HostMapper {
if cfg == nil || len(cfg.Mappings) == 0 {
return nil
}
hosts := hostspkg.NewHosts()
hosts.Logger = logger.Default().WithFields(map[string]any{
"kind": "hosts",
"hosts": cfg.Name,
})
for _, host := range cfg.Mappings {
if host.IP == "" || host.Hostname == "" {
continue
}
ip := net.ParseIP(host.IP)
if ip == nil {
continue
}
hosts.Map(ip, host.Hostname, host.Aliases...)
}
return hosts
}

149
config/parsing/service.go Normal file
View File

@ -0,0 +1,149 @@
package parsing
import (
"strings"
"github.com/go-gost/core/chain"
tls_util "github.com/go-gost/core/common/util/tls"
"github.com/go-gost/core/handler"
"github.com/go-gost/core/listener"
"github.com/go-gost/core/logger"
"github.com/go-gost/core/metadata"
"github.com/go-gost/core/registry"
"github.com/go-gost/core/service"
"github.com/go-gost/x/config"
)
func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
if cfg.Listener == nil {
cfg.Listener = &config.ListenerConfig{
Type: "tcp",
}
}
if cfg.Handler == nil {
cfg.Handler = &config.HandlerConfig{
Type: "auto",
}
}
serviceLogger := logger.Default().WithFields(map[string]any{
"kind": "service",
"service": cfg.Name,
"listener": cfg.Listener.Type,
"handler": cfg.Handler.Type,
})
listenerLogger := serviceLogger.WithFields(map[string]any{
"kind": "listener",
})
tlsCfg := cfg.Listener.TLS
if tlsCfg == nil {
tlsCfg = &config.TLSConfig{}
}
tlsConfig, err := tls_util.LoadServerConfig(
tlsCfg.CertFile, tlsCfg.KeyFile, tlsCfg.CAFile)
if err != nil {
listenerLogger.Error(err)
return nil, err
}
auther := ParseAutherFromAuth(cfg.Listener.Auth)
if cfg.Listener.Auther != "" {
auther = registry.AutherRegistry().Get(cfg.Listener.Auther)
}
ln := registry.ListenerRegistry().Get(cfg.Listener.Type)(
listener.AddrOption(cfg.Addr),
listener.AutherOption(auther),
listener.AuthOption(parseAuth(cfg.Listener.Auth)),
listener.TLSConfigOption(tlsConfig),
listener.AdmissionOption(registry.AdmissionRegistry().Get(cfg.Admission)),
listener.ChainOption(registry.ChainRegistry().Get(cfg.Listener.Chain)),
listener.LoggerOption(listenerLogger),
listener.ServiceOption(cfg.Name),
)
if cfg.Listener.Metadata == nil {
cfg.Listener.Metadata = make(map[string]any)
}
if err := ln.Init(metadata.MapMetadata(cfg.Listener.Metadata)); err != nil {
listenerLogger.Error("init: ", err)
return nil, err
}
handlerLogger := serviceLogger.WithFields(map[string]any{
"kind": "handler",
})
tlsCfg = cfg.Handler.TLS
if tlsCfg == nil {
tlsCfg = &config.TLSConfig{}
}
tlsConfig, err = tls_util.LoadServerConfig(
tlsCfg.CertFile, tlsCfg.KeyFile, tlsCfg.CAFile)
if err != nil {
handlerLogger.Error(err)
return nil, err
}
auther = ParseAutherFromAuth(cfg.Handler.Auth)
if cfg.Handler.Auther != "" {
auther = registry.AutherRegistry().Get(cfg.Handler.Auther)
}
router := (&chain.Router{}).
WithRetries(cfg.Handler.Retries).
// WithTimeout(timeout time.Duration).
WithInterface(cfg.Interface).
WithChain(registry.ChainRegistry().Get(cfg.Handler.Chain)).
WithResolver(registry.ResolverRegistry().Get(cfg.Resolver)).
WithHosts(registry.HostsRegistry().Get(cfg.Hosts)).
WithLogger(handlerLogger)
h := registry.HandlerRegistry().Get(cfg.Handler.Type)(
handler.RouterOption(router),
handler.AutherOption(auther),
handler.AuthOption(parseAuth(cfg.Handler.Auth)),
handler.BypassOption(registry.BypassRegistry().Get(cfg.Bypass)),
handler.TLSConfigOption(tlsConfig),
handler.LoggerOption(handlerLogger),
)
if forwarder, ok := h.(handler.Forwarder); ok {
forwarder.Forward(parseForwarder(cfg.Forwarder))
}
if cfg.Handler.Metadata == nil {
cfg.Handler.Metadata = make(map[string]any)
}
if err := h.Init(metadata.MapMetadata(cfg.Handler.Metadata)); err != nil {
handlerLogger.Error("init: ", err)
return nil, err
}
s := service.NewService(cfg.Name, ln, h,
service.AdmissionOption(registry.AdmissionRegistry().Get(cfg.Admission)),
service.LoggerOption(serviceLogger),
)
serviceLogger.Infof("listening on %s/%s", s.Addr().String(), s.Addr().Network())
return s, nil
}
func parseForwarder(cfg *config.ForwarderConfig) *chain.NodeGroup {
if cfg == nil || len(cfg.Targets) == 0 {
return nil
}
group := &chain.NodeGroup{}
for _, target := range cfg.Targets {
if v := strings.TrimSpace(target); v != "" {
group.AddNode(&chain.Node{
Name: target,
Addr: target,
Marker: &chain.FailMarker{},
})
}
}
return group.WithSelector(parseSelector(cfg.Selector))
}

36
go.mod
View File

@ -5,26 +5,30 @@ go 1.18
require (
github.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed
github.com/docker/libcontainer v2.2.1+incompatible
github.com/go-gost/core v0.0.0-20220316132147-6b2ccaad38a4
github.com/gin-contrib/cors v1.3.1
github.com/gin-gonic/gin v1.7.7
github.com/go-gost/core v0.0.0-20220317144108-bab2906aeb73
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09
github.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7
github.com/go-gost/tls-dissector v0.0.2-0.20211125135007-2b5d5bd9c07e
github.com/golang/snappy v0.0.4
github.com/gorilla/websocket v1.5.0
github.com/lucas-clemente/quic-go v0.25.0
github.com/miekg/dns v1.1.45
github.com/miekg/dns v1.1.47
github.com/milosgajdos/tenus v0.0.3
github.com/rs/xid v1.3.0
github.com/shadowsocks/go-shadowsocks2 v0.1.5
github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
github.com/spf13/viper v1.10.1
github.com/xtaci/kcp-go/v5 v5.6.1
github.com/xtaci/smux v1.5.16
github.com/xtaci/tcpraw v1.2.25
golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
google.golang.org/grpc v1.45.0
google.golang.org/protobuf v1.27.1
gopkg.in/yaml.v2 v2.4.0
)
require (
@ -34,20 +38,33 @@ require (
github.com/cheekybits/genny v1.0.0 // indirect
github.com/coreos/go-iptables v0.5.0 // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.10.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid v1.3.1 // indirect
github.com/klauspost/reedsolomon v1.9.9 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/marten-seemann/qpack v0.2.1 // indirect
github.com/marten-seemann/qtls-go1-16 v0.1.4 // indirect
github.com/marten-seemann/qtls-go1-17 v0.1.0 // indirect
github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_golang v1.12.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
@ -55,14 +72,21 @@ require (
github.com/prometheus/procfs v0.7.3 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/spf13/afero v1.8.2 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/templexxx/cpu v0.0.7 // indirect
github.com/templexxx/xorsimd v0.4.1 // indirect
github.com/tjfoc/gmsm v1.3.2 // indirect
golang.org/x/mod v0.5.1 // indirect
github.com/ugorji/go/codec v1.2.7 // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/sys v0.0.0-20220315194320-039c03cc5b86 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.9 // indirect
golang.org/x/tools v0.1.10 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350 // indirect
gopkg.in/ini.v1 v1.66.4 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
)

80
go.sum
View File

@ -99,13 +99,20 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/cors v1.3.1 h1:doAsuITavI4IOcd0Y19U4B+O0dNWihRyX//nn4sEmgA=
github.com/gin-contrib/cors v1.3.1/go.mod h1:jjEJ4268OPZUcU7k9Pm653S7lXUGcqMADzFA61xsmDk=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gost/core v0.0.0-20220316132147-6b2ccaad38a4 h1:u2ryD5Csx21bvLFadzezwf5/TniK8YmMK1jmhfJmTLg=
github.com/go-gost/core v0.0.0-20220316132147-6b2ccaad38a4/go.mod h1:+Eqgm24sZ61yY9uPhRIPM79Ig+r2Rk5QIOcZWLL+5q0=
github.com/go-gost/core v0.0.0-20220317144108-bab2906aeb73 h1:N9iL7Jf+ahGBieyKu2iUqPVQFQzUjqGKl/lZM+YiSEo=
github.com/go-gost/core v0.0.0-20220317144108-bab2906aeb73/go.mod h1:+Eqgm24sZ61yY9uPhRIPM79Ig+r2Rk5QIOcZWLL+5q0=
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09 h1:A95M6UWcfZgOuJkQ7QLfG0Hs5peWIUSysCDNz4pfe04=
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09/go.mod h1:1G6I7HP7VFVxveGkoK8mnprnJqSqJjdcASKsdUn4Pp4=
github.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7 h1:itaaJhQJ19kUXEB4Igb0EbY8m+1Py2AaNNSBds/9gk4=
@ -118,6 +125,16 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-playground/validator/v10 v10.10.1 h1:uA0+amWMiglNZKZ9FJRKUAe9U3RX91eVn1JYXMWt7ig=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
@ -197,13 +214,18 @@ github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpg
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
@ -217,14 +239,21 @@ github.com/klauspost/reedsolomon v1.9.9 h1:qCL7LZlv17xMixl55nq2/Oa1Y86nfO8EqDfv2
github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/lucas-clemente/quic-go v0.25.0 h1:K+X9Gvd7JXsOHtU0N2icZ2Nw3rx82uBej3mP4CLgibc=
github.com/lucas-clemente/quic-go v0.25.0/go.mod h1:YtzP8bxRVCBlO77yRanE264+fY/T2U9ZlW1AaHOsMOg=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/marten-seemann/qpack v0.2.1 h1:jvTsT/HpCn2UZJdP+UUB53FfUUgeOyG5K1ns0OJOGVs=
github.com/marten-seemann/qpack v0.2.1/go.mod h1:F7Gl5L1jIgN1D11ucXefiuJS9UMVP2opoCp2jDKb7wc=
@ -235,19 +264,26 @@ github.com/marten-seemann/qtls-go1-17 v0.1.0 h1:P9ggrs5xtwiqXv/FHNwntmuLMNq3KaSI
github.com/marten-seemann/qtls-go1-17 v0.1.0/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8=
github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1 h1:EnzzN9fPUkUck/1CuY1FlzBaIYMoiBsdwTNmNGkwUUM=
github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1/go.mod h1:PUhIQk19LoFt2174H4+an8TYvWOGjb/hHwphBeaDHwI=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.1.45 h1:g5fRIhm9nx7g8osrAvgb16QJfmyMsyOCb+J7LSv+Qzk=
github.com/miekg/dns v1.1.45/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/miekg/dns v1.1.47 h1:J9bWiXbqMbnZPcY8Qi2E3EWIBsIm6MZzzJB9VRg5gL8=
github.com/milosgajdos/tenus v0.0.3 h1:jmaJzwaY1DUyYVD0lM4U+uvP2kkEg1VahDqRFxIkVBE=
github.com/milosgajdos/tenus v0.0.3/go.mod h1:eIjx29vNeDOYWJuCnaHY2r4fq5egetV26ry3on7p8qY=
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 h1:ULR/QWMgcgRiZLUjSSJMU+fW+RDMstRdmnDWj9Q+AsA=
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@ -268,6 +304,8 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@ -303,6 +341,7 @@ github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4=
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
@ -343,6 +382,15 @@ github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8/go.mod h1:P5HUIBuIWK
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.10.1 h1:nuJZuYpG7gTj/XqiUwg8bA0cp1+M2mC3J4g5luUYBKk=
github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@ -352,6 +400,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/templexxx/cpu v0.0.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
github.com/templexxx/cpu v0.0.7 h1:pUEZn8JBy/w5yzdYWgx+0m0xL9uk6j4K91C5kOViAzo=
@ -360,6 +410,9 @@ github.com/templexxx/xorsimd v0.4.1 h1:iUZcywbOYDRAZUasAs2eSCUW8eobuZDy0I9FJiORk
github.com/templexxx/xorsimd v0.4.1/go.mod h1:W+ffZz8jJMH2SXwuKu9WhygqBMbFnp14G2fqEr8qaNo=
github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
github.com/xtaci/kcp-go/v5 v5.6.1 h1:Pwn0aoeNSPF9dTS7IgiPXn0HEtaIlVb6y5UKWPsx8bI=
@ -397,8 +450,7 @@ golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70 h1:syTAU9FwmvzEoIYMqcPHOcVm4H3U5u90WsvuYgwpETU=
golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -431,8 +483,7 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -471,8 +522,7 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -509,6 +559,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -518,6 +569,7 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -609,8 +661,7 @@ golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.9 h1:j9KsMiaP1c3B0OTQGth0/k+miLGTgLsAFUCrF2vLcF8=
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -717,9 +768,13 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
@ -727,6 +782,7 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=