add reload and plugin support for hop

This commit is contained in:
ginuerzh 2023-09-28 21:04:15 +08:00
parent ddc3c9392e
commit ea585fc25d
88 changed files with 2208 additions and 1538 deletions

View File

@ -10,18 +10,18 @@ import (
"github.com/go-gost/core/admission" "github.com/go-gost/core/admission"
"github.com/go-gost/core/logger" "github.com/go-gost/core/logger"
"github.com/go-gost/plugin/admission/proto" "github.com/go-gost/plugin/admission/proto"
"github.com/go-gost/x/internal/util/plugin" "github.com/go-gost/x/internal/plugin"
"google.golang.org/grpc" "google.golang.org/grpc"
) )
type grpcPluginAdmission struct { type grpcPlugin struct {
conn grpc.ClientConnInterface conn grpc.ClientConnInterface
client proto.AdmissionClient client proto.AdmissionClient
log logger.Logger log logger.Logger
} }
// NewGRPCPluginAdmission creates an Admission plugin based on gRPC. // NewGRPCPlugin creates an Admission plugin based on gRPC.
func NewGRPCPluginAdmission(name string, addr string, opts ...plugin.Option) admission.Admission { func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) admission.Admission {
var options plugin.Options var options plugin.Options
for _, opt := range opts { for _, opt := range opts {
opt(&options) opt(&options)
@ -36,7 +36,7 @@ func NewGRPCPluginAdmission(name string, addr string, opts ...plugin.Option) adm
log.Error(err) log.Error(err)
} }
p := &grpcPluginAdmission{ p := &grpcPlugin{
conn: conn, conn: conn,
log: log, log: log,
} }
@ -46,7 +46,7 @@ func NewGRPCPluginAdmission(name string, addr string, opts ...plugin.Option) adm
return p return p
} }
func (p *grpcPluginAdmission) Admit(ctx context.Context, addr string) bool { func (p *grpcPlugin) Admit(ctx context.Context, addr string) bool {
if p.client == nil { if p.client == nil {
return false return false
} }
@ -62,36 +62,36 @@ func (p *grpcPluginAdmission) Admit(ctx context.Context, addr string) bool {
return r.Ok return r.Ok
} }
func (p *grpcPluginAdmission) Close() error { func (p *grpcPlugin) Close() error {
if closer, ok := p.conn.(io.Closer); ok { if closer, ok := p.conn.(io.Closer); ok {
return closer.Close() return closer.Close()
} }
return nil return nil
} }
type httpAdmissionRequest struct { type httpPluginRequest struct {
Addr string `json:"addr"` Addr string `json:"addr"`
} }
type httpAdmissionResponse struct { type httpPluginResponse struct {
OK bool `json:"ok"` OK bool `json:"ok"`
} }
type httpPluginAdmission struct { type httpPlugin struct {
url string url string
client *http.Client client *http.Client
header http.Header header http.Header
log logger.Logger log logger.Logger
} }
// NewHTTPPluginAdmission creates an Admission plugin based on HTTP. // NewHTTPPlugin creates an Admission plugin based on HTTP.
func NewHTTPPluginAdmission(name string, url string, opts ...plugin.Option) admission.Admission { func NewHTTPPlugin(name string, url string, opts ...plugin.Option) admission.Admission {
var options plugin.Options var options plugin.Options
for _, opt := range opts { for _, opt := range opts {
opt(&options) opt(&options)
} }
return &httpPluginAdmission{ return &httpPlugin{
url: url, url: url,
client: plugin.NewHTTPClient(&options), client: plugin.NewHTTPClient(&options),
header: options.Header, header: options.Header,
@ -102,12 +102,12 @@ func NewHTTPPluginAdmission(name string, url string, opts ...plugin.Option) admi
} }
} }
func (p *httpPluginAdmission) Admit(ctx context.Context, addr string) (ok bool) { func (p *httpPlugin) Admit(ctx context.Context, addr string) (ok bool) {
if p.client == nil { if p.client == nil {
return return
} }
rb := httpAdmissionRequest{ rb := httpPluginRequest{
Addr: addr, Addr: addr,
} }
v, err := json.Marshal(&rb) v, err := json.Marshal(&rb)
@ -134,7 +134,7 @@ func (p *httpPluginAdmission) Admit(ctx context.Context, addr string) (ok bool)
return return
} }
res := httpAdmissionResponse{} res := httpPluginResponse{}
if err := json.NewDecoder(resp.Body).Decode(&res); err != nil { if err := json.NewDecoder(resp.Body).Decode(&res); err != nil {
return return
} }

View File

@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-gost/x/config" "github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing" parser "github.com/go-gost/x/config/parsing/admission"
"github.com/go-gost/x/registry" "github.com/go-gost/x/registry"
) )
@ -40,7 +40,7 @@ func createAdmission(ctx *gin.Context) {
return return
} }
v := parsing.ParseAdmission(&req.Data) v := parser.ParseAdmission(&req.Data)
if err := registry.AdmissionRegistry().Register(req.Data.Name, v); err != nil { if err := registry.AdmissionRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup) writeError(ctx, ErrDup)
@ -94,7 +94,7 @@ func updateAdmission(ctx *gin.Context) {
req.Data.Name = req.Admission req.Data.Name = req.Admission
v := parsing.ParseAdmission(&req.Data) v := parser.ParseAdmission(&req.Data)
registry.AdmissionRegistry().Unregister(req.Admission) registry.AdmissionRegistry().Unregister(req.Admission)

View File

@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-gost/x/config" "github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing" parser "github.com/go-gost/x/config/parsing/auth"
"github.com/go-gost/x/registry" "github.com/go-gost/x/registry"
) )
@ -40,7 +40,7 @@ func createAuther(ctx *gin.Context) {
return return
} }
v := parsing.ParseAuther(&req.Data) v := parser.ParseAuther(&req.Data)
if err := registry.AutherRegistry().Register(req.Data.Name, v); err != nil { if err := registry.AutherRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup) writeError(ctx, ErrDup)
return return
@ -93,7 +93,7 @@ func updateAuther(ctx *gin.Context) {
req.Data.Name = req.Auther req.Data.Name = req.Auther
v := parsing.ParseAuther(&req.Data) v := parser.ParseAuther(&req.Data)
registry.AutherRegistry().Unregister(req.Auther) registry.AutherRegistry().Unregister(req.Auther)
if err := registry.AutherRegistry().Register(req.Auther, v); err != nil { if err := registry.AutherRegistry().Register(req.Auther, v); err != nil {

View File

@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-gost/x/config" "github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing" parser "github.com/go-gost/x/config/parsing/bypass"
"github.com/go-gost/x/registry" "github.com/go-gost/x/registry"
) )
@ -40,7 +40,7 @@ func createBypass(ctx *gin.Context) {
return return
} }
v := parsing.ParseBypass(&req.Data) v := parser.ParseBypass(&req.Data)
if err := registry.BypassRegistry().Register(req.Data.Name, v); err != nil { if err := registry.BypassRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup) writeError(ctx, ErrDup)
@ -94,7 +94,7 @@ func updateBypass(ctx *gin.Context) {
req.Data.Name = req.Bypass req.Data.Name = req.Bypass
v := parsing.ParseBypass(&req.Data) v := parser.ParseBypass(&req.Data)
registry.BypassRegistry().Unregister(req.Bypass) registry.BypassRegistry().Unregister(req.Bypass)

View File

@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-gost/x/config" "github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing" parser "github.com/go-gost/x/config/parsing/chain"
"github.com/go-gost/x/registry" "github.com/go-gost/x/registry"
) )
@ -40,7 +40,7 @@ func createChain(ctx *gin.Context) {
return return
} }
v, err := parsing.ParseChain(&req.Data) v, err := parser.ParseChain(&req.Data)
if err != nil { if err != nil {
writeError(ctx, ErrCreate) writeError(ctx, ErrCreate)
return return
@ -99,7 +99,7 @@ func updateChain(ctx *gin.Context) {
req.Data.Name = req.Chain req.Data.Name = req.Chain
v, err := parsing.ParseChain(&req.Data) v, err := parser.ParseChain(&req.Data)
if err != nil { if err != nil {
writeError(ctx, ErrCreate) writeError(ctx, ErrCreate)
return return

View File

@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-gost/x/config" "github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing" parser "github.com/go-gost/x/config/parsing/limiter"
"github.com/go-gost/x/registry" "github.com/go-gost/x/registry"
) )
@ -40,7 +40,7 @@ func createConnLimiter(ctx *gin.Context) {
return return
} }
v := parsing.ParseConnLimiter(&req.Data) v := parser.ParseConnLimiter(&req.Data)
if err := registry.ConnLimiterRegistry().Register(req.Data.Name, v); err != nil { if err := registry.ConnLimiterRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup) writeError(ctx, ErrDup)
@ -94,7 +94,7 @@ func updateConnLimiter(ctx *gin.Context) {
req.Data.Name = req.Limiter req.Data.Name = req.Limiter
v := parsing.ParseConnLimiter(&req.Data) v := parser.ParseConnLimiter(&req.Data)
registry.ConnLimiterRegistry().Unregister(req.Limiter) registry.ConnLimiterRegistry().Unregister(req.Limiter)

View File

@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-gost/x/config" "github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing" parser "github.com/go-gost/x/config/parsing/hop"
"github.com/go-gost/x/registry" "github.com/go-gost/x/registry"
) )
@ -40,7 +40,7 @@ func createHop(ctx *gin.Context) {
return return
} }
v, err := parsing.ParseHop(&req.Data) v, err := parser.ParseHop(&req.Data)
if err != nil { if err != nil {
writeError(ctx, ErrCreate) writeError(ctx, ErrCreate)
return return
@ -99,7 +99,7 @@ func updateHop(ctx *gin.Context) {
req.Data.Name = req.Hop req.Data.Name = req.Hop
v, err := parsing.ParseHop(&req.Data) v, err := parser.ParseHop(&req.Data)
if err != nil { if err != nil {
writeError(ctx, ErrCreate) writeError(ctx, ErrCreate)
return return

View File

@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-gost/x/config" "github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing" parser "github.com/go-gost/x/config/parsing/hosts"
"github.com/go-gost/x/registry" "github.com/go-gost/x/registry"
) )
@ -40,7 +40,7 @@ func createHosts(ctx *gin.Context) {
return return
} }
v := parsing.ParseHosts(&req.Data) v := parser.ParseHostMapper(&req.Data)
if err := registry.HostsRegistry().Register(req.Data.Name, v); err != nil { if err := registry.HostsRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup) writeError(ctx, ErrDup)
@ -94,7 +94,7 @@ func updateHosts(ctx *gin.Context) {
req.Data.Name = req.Hosts req.Data.Name = req.Hosts
v := parsing.ParseHosts(&req.Data) v := parser.ParseHostMapper(&req.Data)
registry.HostsRegistry().Unregister(req.Hosts) registry.HostsRegistry().Unregister(req.Hosts)

View File

@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-gost/x/config" "github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing" parser "github.com/go-gost/x/config/parsing/ingress"
"github.com/go-gost/x/registry" "github.com/go-gost/x/registry"
) )
@ -40,7 +40,7 @@ func createIngress(ctx *gin.Context) {
return return
} }
v := parsing.ParseIngress(&req.Data) v := parser.ParseIngress(&req.Data)
if err := registry.IngressRegistry().Register(req.Data.Name, v); err != nil { if err := registry.IngressRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup) writeError(ctx, ErrDup)
@ -94,7 +94,7 @@ func updateIngress(ctx *gin.Context) {
req.Data.Name = req.Ingress req.Data.Name = req.Ingress
v := parsing.ParseIngress(&req.Data) v := parser.ParseIngress(&req.Data)
registry.IngressRegistry().Unregister(req.Ingress) registry.IngressRegistry().Unregister(req.Ingress)

View File

@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-gost/x/config" "github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing" parser "github.com/go-gost/x/config/parsing/limiter"
"github.com/go-gost/x/registry" "github.com/go-gost/x/registry"
) )
@ -40,7 +40,7 @@ func createLimiter(ctx *gin.Context) {
return return
} }
v := parsing.ParseTrafficLimiter(&req.Data) v := parser.ParseTrafficLimiter(&req.Data)
if err := registry.TrafficLimiterRegistry().Register(req.Data.Name, v); err != nil { if err := registry.TrafficLimiterRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup) writeError(ctx, ErrDup)
@ -94,7 +94,7 @@ func updateLimiter(ctx *gin.Context) {
req.Data.Name = req.Limiter req.Data.Name = req.Limiter
v := parsing.ParseTrafficLimiter(&req.Data) v := parser.ParseTrafficLimiter(&req.Data)
registry.TrafficLimiterRegistry().Unregister(req.Limiter) registry.TrafficLimiterRegistry().Unregister(req.Limiter)

View File

@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-gost/x/config" "github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing" parser "github.com/go-gost/x/config/parsing/limiter"
"github.com/go-gost/x/registry" "github.com/go-gost/x/registry"
) )
@ -40,7 +40,7 @@ func createRateLimiter(ctx *gin.Context) {
return return
} }
v := parsing.ParseRateLimiter(&req.Data) v := parser.ParseRateLimiter(&req.Data)
if err := registry.RateLimiterRegistry().Register(req.Data.Name, v); err != nil { if err := registry.RateLimiterRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup) writeError(ctx, ErrDup)
@ -94,7 +94,7 @@ func updateRateLimiter(ctx *gin.Context) {
req.Data.Name = req.Limiter req.Data.Name = req.Limiter
v := parsing.ParseRateLimiter(&req.Data) v := parser.ParseRateLimiter(&req.Data)
registry.RateLimiterRegistry().Unregister(req.Limiter) registry.RateLimiterRegistry().Unregister(req.Limiter)

View File

@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-gost/x/config" "github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing" parser "github.com/go-gost/x/config/parsing/resolver"
"github.com/go-gost/x/registry" "github.com/go-gost/x/registry"
) )
@ -40,7 +40,7 @@ func createResolver(ctx *gin.Context) {
return return
} }
v, err := parsing.ParseResolver(&req.Data) v, err := parser.ParseResolver(&req.Data)
if err != nil { if err != nil {
writeError(ctx, ErrCreate) writeError(ctx, ErrCreate)
return return
@ -98,7 +98,7 @@ func updateResolver(ctx *gin.Context) {
req.Data.Name = req.Resolver req.Data.Name = req.Resolver
v, err := parsing.ParseResolver(&req.Data) v, err := parser.ParseResolver(&req.Data)
if err != nil { if err != nil {
writeError(ctx, ErrCreate) writeError(ctx, ErrCreate)
return return

View File

@ -5,7 +5,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/go-gost/x/config" "github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing" parser "github.com/go-gost/x/config/parsing/service"
"github.com/go-gost/x/registry" "github.com/go-gost/x/registry"
) )
@ -45,7 +45,7 @@ func createService(ctx *gin.Context) {
return return
} }
svc, err := parsing.ParseService(&req.Data) svc, err := parser.ParseService(&req.Data)
if err != nil { if err != nil {
writeError(ctx, ErrCreate) writeError(ctx, ErrCreate)
return return
@ -108,7 +108,7 @@ func updateService(ctx *gin.Context) {
req.Data.Name = req.Service req.Data.Name = req.Service
svc, err := parsing.ParseService(&req.Data) svc, err := parser.ParseService(&req.Data)
if err != nil { if err != nil {
writeError(ctx, ErrCreate) writeError(ctx, ErrCreate)
return return

View File

@ -10,19 +10,19 @@ import (
"github.com/go-gost/core/auth" "github.com/go-gost/core/auth"
"github.com/go-gost/core/logger" "github.com/go-gost/core/logger"
"github.com/go-gost/plugin/auth/proto" "github.com/go-gost/plugin/auth/proto"
"github.com/go-gost/x/internal/plugin"
auth_util "github.com/go-gost/x/internal/util/auth" auth_util "github.com/go-gost/x/internal/util/auth"
"github.com/go-gost/x/internal/util/plugin"
"google.golang.org/grpc" "google.golang.org/grpc"
) )
type grpcPluginAuthenticator struct { type grpcPlugin struct {
conn grpc.ClientConnInterface conn grpc.ClientConnInterface
client proto.AuthenticatorClient client proto.AuthenticatorClient
log logger.Logger log logger.Logger
} }
// NewGRPCPluginAuthenticator creates an Authenticator plugin based on gRPC. // NewGRPCPlugin creates an Authenticator plugin based on gRPC.
func NewGRPCPluginAuthenticator(name string, addr string, opts ...plugin.Option) auth.Authenticator { func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) auth.Authenticator {
var options plugin.Options var options plugin.Options
for _, opt := range opts { for _, opt := range opts {
opt(&options) opt(&options)
@ -37,7 +37,7 @@ func NewGRPCPluginAuthenticator(name string, addr string, opts ...plugin.Option)
log.Error(err) log.Error(err)
} }
p := &grpcPluginAuthenticator{ p := &grpcPlugin{
conn: conn, conn: conn,
log: log, log: log,
} }
@ -49,7 +49,7 @@ func NewGRPCPluginAuthenticator(name string, addr string, opts ...plugin.Option)
} }
// Authenticate checks the validity of the provided user-password pair. // Authenticate checks the validity of the provided user-password pair.
func (p *grpcPluginAuthenticator) Authenticate(ctx context.Context, user, password string) (string, bool) { func (p *grpcPlugin) Authenticate(ctx context.Context, user, password string) (string, bool) {
if p.client == nil { if p.client == nil {
return "", false return "", false
} }
@ -67,39 +67,39 @@ func (p *grpcPluginAuthenticator) Authenticate(ctx context.Context, user, passwo
return r.Id, r.Ok return r.Id, r.Ok
} }
func (p *grpcPluginAuthenticator) Close() error { func (p *grpcPlugin) Close() error {
if closer, ok := p.conn.(io.Closer); ok { if closer, ok := p.conn.(io.Closer); ok {
return closer.Close() return closer.Close()
} }
return nil return nil
} }
type httpAutherRequest struct { type httpPluginRequest struct {
Username string `json:"username"` Username string `json:"username"`
Password string `json:"password"` Password string `json:"password"`
Client string `json:"client"` Client string `json:"client"`
} }
type httpAutherResponse struct { type httpPluginResponse struct {
OK bool `json:"ok"` OK bool `json:"ok"`
ID string `json:"id"` ID string `json:"id"`
} }
type httpPluginAuther struct { type httpPlugin struct {
url string url string
client *http.Client client *http.Client
header http.Header header http.Header
log logger.Logger log logger.Logger
} }
// NewHTTPPluginAuthenticator creates an Authenticator plugin based on HTTP. // NewHTTPPlugin creates an Authenticator plugin based on HTTP.
func NewHTTPPluginAuthenticator(name string, url string, opts ...plugin.Option) auth.Authenticator { func NewHTTPPlugin(name string, url string, opts ...plugin.Option) auth.Authenticator {
var options plugin.Options var options plugin.Options
for _, opt := range opts { for _, opt := range opts {
opt(&options) opt(&options)
} }
return &httpPluginAuther{ return &httpPlugin{
url: url, url: url,
client: plugin.NewHTTPClient(&options), client: plugin.NewHTTPClient(&options),
header: options.Header, header: options.Header,
@ -110,12 +110,12 @@ func NewHTTPPluginAuthenticator(name string, url string, opts ...plugin.Option)
} }
} }
func (p *httpPluginAuther) Authenticate(ctx context.Context, user, password string) (id string, ok bool) { func (p *httpPlugin) Authenticate(ctx context.Context, user, password string) (id string, ok bool) {
if p.client == nil { if p.client == nil {
return return
} }
rb := httpAutherRequest{ rb := httpPluginRequest{
Username: user, Username: user,
Password: password, Password: password,
Client: string(auth_util.ClientAddrFromContext(ctx)), Client: string(auth_util.ClientAddrFromContext(ctx)),
@ -144,7 +144,7 @@ func (p *httpPluginAuther) Authenticate(ctx context.Context, user, password stri
return return
} }
res := httpAutherResponse{} res := httpPluginResponse{}
if err := json.NewDecoder(resp.Body).Decode(&res); err != nil { if err := json.NewDecoder(resp.Body).Decode(&res); err != nil {
return return
} }

View File

@ -13,7 +13,6 @@ import (
"github.com/go-gost/core/logger" "github.com/go-gost/core/logger"
"github.com/go-gost/x/internal/loader" "github.com/go-gost/x/internal/loader"
"github.com/go-gost/x/internal/matcher" "github.com/go-gost/x/internal/matcher"
"google.golang.org/grpc"
) )
type options struct { type options struct {
@ -22,7 +21,6 @@ type options struct {
fileLoader loader.Loader fileLoader loader.Loader
redisLoader loader.Loader redisLoader loader.Loader
httpLoader loader.Loader httpLoader loader.Loader
client *grpc.ClientConn
period time.Duration period time.Duration
logger logger.Logger logger logger.Logger
} }
@ -65,12 +63,6 @@ func HTTPLoaderOption(httpLoader loader.Loader) Option {
} }
} }
func PluginConnOption(c *grpc.ClientConn) Option {
return func(opts *options) {
opts.client = c
}
}
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

View File

@ -11,18 +11,18 @@ import (
"github.com/go-gost/core/logger" "github.com/go-gost/core/logger"
"github.com/go-gost/plugin/bypass/proto" "github.com/go-gost/plugin/bypass/proto"
auth_util "github.com/go-gost/x/internal/util/auth" auth_util "github.com/go-gost/x/internal/util/auth"
"github.com/go-gost/x/internal/util/plugin" "github.com/go-gost/x/internal/plugin"
"google.golang.org/grpc" "google.golang.org/grpc"
) )
type grpcPluginBypass struct { type grpcPlugin struct {
conn grpc.ClientConnInterface conn grpc.ClientConnInterface
client proto.BypassClient client proto.BypassClient
log logger.Logger log logger.Logger
} }
// NewGRPCPluginBypass creates a Bypass plugin based on gRPC. // NewGRPCPlugin creates a Bypass plugin based on gRPC.
func NewGRPCPluginBypass(name string, addr string, opts ...plugin.Option) bypass.Bypass { func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) bypass.Bypass {
var options plugin.Options var options plugin.Options
for _, opt := range opts { for _, opt := range opts {
opt(&options) opt(&options)
@ -37,7 +37,7 @@ func NewGRPCPluginBypass(name string, addr string, opts ...plugin.Option) bypass
log.Error(err) log.Error(err)
} }
p := &grpcPluginBypass{ p := &grpcPlugin{
conn: conn, conn: conn,
log: log, log: log,
} }
@ -47,7 +47,7 @@ func NewGRPCPluginBypass(name string, addr string, opts ...plugin.Option) bypass
return p return p
} }
func (p *grpcPluginBypass) Contains(ctx context.Context, addr string) bool { func (p *grpcPlugin) Contains(ctx context.Context, addr string) bool {
if p.client == nil { if p.client == nil {
return true return true
} }
@ -64,37 +64,37 @@ func (p *grpcPluginBypass) Contains(ctx context.Context, addr string) bool {
return r.Ok return r.Ok
} }
func (p *grpcPluginBypass) Close() error { func (p *grpcPlugin) Close() error {
if closer, ok := p.conn.(io.Closer); ok { if closer, ok := p.conn.(io.Closer); ok {
return closer.Close() return closer.Close()
} }
return nil return nil
} }
type httpBypassRequest struct { type httpPluginRequest struct {
Addr string `json:"addr"` Addr string `json:"addr"`
Client string `json:"client"` Client string `json:"client"`
} }
type httpBypassResponse struct { type httpPluginResponse struct {
OK bool `json:"ok"` OK bool `json:"ok"`
} }
type httpPluginBypass struct { type httpPlugin struct {
url string url string
client *http.Client client *http.Client
header http.Header header http.Header
log logger.Logger log logger.Logger
} }
// NewHTTPPluginBypass creates an Bypass plugin based on HTTP. // NewHTTPPlugin creates an Bypass plugin based on HTTP.
func NewHTTPPluginBypass(name string, url string, opts ...plugin.Option) bypass.Bypass { func NewHTTPPlugin(name string, url string, opts ...plugin.Option) bypass.Bypass {
var options plugin.Options var options plugin.Options
for _, opt := range opts { for _, opt := range opts {
opt(&options) opt(&options)
} }
return &httpPluginBypass{ return &httpPlugin{
url: url, url: url,
client: plugin.NewHTTPClient(&options), client: plugin.NewHTTPClient(&options),
header: options.Header, header: options.Header,
@ -105,12 +105,12 @@ func NewHTTPPluginBypass(name string, url string, opts ...plugin.Option) bypass.
} }
} }
func (p *httpPluginBypass) Contains(ctx context.Context, addr string) (ok bool) { func (p *httpPlugin) Contains(ctx context.Context, addr string) (ok bool) {
if p.client == nil { if p.client == nil {
return return
} }
rb := httpBypassRequest{ rb := httpPluginRequest{
Addr: addr, Addr: addr,
Client: string(auth_util.IDFromContext(ctx)), Client: string(auth_util.IDFromContext(ctx)),
} }
@ -138,7 +138,7 @@ func (p *httpPluginBypass) Contains(ctx context.Context, addr string) (ok bool)
return return
} }
res := httpBypassResponse{} res := httpPluginResponse{}
if err := json.NewDecoder(resp.Body).Decode(&res); err != nil { if err := json.NewDecoder(resp.Body).Decode(&res); err != nil {
return return
} }

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"github.com/go-gost/core/chain" "github.com/go-gost/core/chain"
"github.com/go-gost/core/hop"
"github.com/go-gost/core/logger" "github.com/go-gost/core/logger"
"github.com/go-gost/core/metadata" "github.com/go-gost/core/metadata"
"github.com/go-gost/core/selector" "github.com/go-gost/core/selector"
@ -38,7 +39,7 @@ type chainNamer interface {
type Chain struct { type Chain struct {
name string name string
hops []chain.Hop hops []hop.Hop
marker selector.Marker marker selector.Marker
metadata metadata.Metadata metadata metadata.Metadata
logger logger.Logger logger logger.Logger
@ -60,7 +61,7 @@ func NewChain(name string, opts ...ChainOption) *Chain {
} }
} }
func (c *Chain) AddHop(hop chain.Hop) { func (c *Chain) AddHop(hop hop.Hop) {
c.hops = append(c.hops, hop) c.hops = append(c.hops, hop)
} }
@ -84,8 +85,8 @@ func (c *Chain) Route(ctx context.Context, network, address string) chain.Route
} }
rt := NewRoute(ChainRouteOption(c)) rt := NewRoute(ChainRouteOption(c))
for _, hop := range c.hops { for _, h := range c.hops {
node := hop.Select(ctx, chain.AddrSelectOption(address)) node := h.Select(ctx, hop.AddrSelectOption(address))
if node == nil { if node == nil {
return rt return rt
} }

View File

@ -1,138 +0,0 @@
package chain
import (
"context"
"net"
"strings"
"github.com/go-gost/core/bypass"
"github.com/go-gost/core/chain"
"github.com/go-gost/core/logger"
"github.com/go-gost/core/selector"
)
type HopOptions struct {
bypass bypass.Bypass
selector selector.Selector[*chain.Node]
logger logger.Logger
}
type HopOption func(*HopOptions)
func BypassHopOption(bp bypass.Bypass) HopOption {
return func(o *HopOptions) {
o.bypass = bp
}
}
func SelectorHopOption(s selector.Selector[*chain.Node]) HopOption {
return func(o *HopOptions) {
o.selector = s
}
}
func LoggerHopOption(logger logger.Logger) HopOption {
return func(opts *HopOptions) {
opts.logger = logger
}
}
type chainHop struct {
nodes []*chain.Node
options HopOptions
}
func NewChainHop(nodes []*chain.Node, opts ...HopOption) chain.Hop {
var options HopOptions
for _, opt := range opts {
if opt != nil {
opt(&options)
}
}
hop := &chainHop{
nodes: nodes,
options: options,
}
return hop
}
func (p *chainHop) Nodes() []*chain.Node {
return p.nodes
}
func (p *chainHop) Select(ctx context.Context, opts ...chain.SelectOption) *chain.Node {
var options chain.SelectOptions
for _, opt := range opts {
opt(&options)
}
if p == nil || len(p.nodes) == 0 {
return nil
}
// hop level bypass
if p.options.bypass != nil &&
p.options.bypass.Contains(ctx, options.Addr) {
return nil
}
filters := p.nodes
if host := options.Host; host != "" {
filters = nil
if v, _, _ := net.SplitHostPort(host); v != "" {
host = v
}
var nodes []*chain.Node
for _, node := range p.nodes {
if node == nil {
continue
}
vhost := node.Options().Host
if vhost == "" {
nodes = append(nodes, node)
continue
}
if vhost == host ||
vhost[0] == '.' && strings.HasSuffix(host, vhost[1:]) {
filters = append(filters, node)
}
}
if len(filters) == 0 {
filters = nodes
}
} else if protocol := options.Protocol; protocol != "" {
filters = nil
for _, node := range p.nodes {
if node == nil {
continue
}
if node.Options().Protocol == protocol {
filters = append(filters, node)
}
}
}
var nodes []*chain.Node
for _, node := range filters {
if node == nil {
continue
}
// node level bypass
if node.Options().Bypass != nil &&
node.Options().Bypass.Contains(ctx, options.Addr) {
continue
}
nodes = append(nodes, node)
}
if len(nodes) == 0 {
return nil
}
if s := p.options.selector; s != nil {
return s.Select(ctx, nodes...)
}
return nodes[0]
}

View File

@ -371,9 +371,7 @@ type ServiceConfig struct {
} }
type ChainConfig struct { type ChainConfig struct {
Name string `json:"name"` Name string `json:"name"`
// REMOVED since beta.6
// Selector *SelectorConfig `yaml:",omitempty" json:"selector,omitempty"`
Hops []*HopConfig `json:"hops"` Hops []*HopConfig `json:"hops"`
Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"` Metadata map[string]any `yaml:",omitempty" json:"metadata,omitempty"`
} }
@ -393,6 +391,11 @@ type HopConfig struct {
Resolver string `yaml:",omitempty" json:"resolver,omitempty"` Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
Hosts string `yaml:",omitempty" json:"hosts,omitempty"` Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
Nodes []*NodeConfig `yaml:",omitempty" json:"nodes,omitempty"` Nodes []*NodeConfig `yaml:",omitempty" json:"nodes,omitempty"`
Reload time.Duration `yaml:",omitempty" json:"reload,omitempty"`
File *FileLoader `yaml:",omitempty" json:"file,omitempty"`
Redis *RedisLoader `yaml:",omitempty" json:"redis,omitempty"`
HTTP *HTTPLoader `yaml:"http,omitempty" json:"http,omitempty"`
Plugin *PluginConfig `yaml:",omitempty" json:"plugin,omitempty"`
} }
type NodeConfig struct { type NodeConfig struct {

View File

@ -0,0 +1,87 @@
package admission
import (
"crypto/tls"
"strings"
"github.com/go-gost/core/admission"
"github.com/go-gost/core/logger"
xadmission "github.com/go-gost/x/admission"
"github.com/go-gost/x/config"
"github.com/go-gost/x/internal/loader"
"github.com/go-gost/x/internal/plugin"
"github.com/go-gost/x/registry"
)
func ParseAdmission(cfg *config.AdmissionConfig) admission.Admission {
if cfg == nil {
return nil
}
if cfg.Plugin != nil {
var tlsCfg *tls.Config
if cfg.Plugin.TLS != nil {
tlsCfg = &tls.Config{
ServerName: cfg.Plugin.TLS.ServerName,
InsecureSkipVerify: !cfg.Plugin.TLS.Secure,
}
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return xadmission.NewHTTPPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
)
default:
return xadmission.NewGRPCPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),
)
}
}
opts := []xadmission.Option{
xadmission.MatchersOption(cfg.Matchers),
xadmission.WhitelistOption(cfg.Reverse || cfg.Whitelist),
xadmission.ReloadPeriodOption(cfg.Reload),
xadmission.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "admission",
"admission": cfg.Name,
})),
}
if cfg.File != nil && cfg.File.Path != "" {
opts = append(opts, xadmission.FileLoaderOption(loader.FileLoader(cfg.File.Path)))
}
if cfg.Redis != nil && cfg.Redis.Addr != "" {
opts = append(opts, xadmission.RedisLoaderOption(loader.RedisSetLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
}
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
opts = append(opts, xadmission.HTTPLoaderOption(loader.HTTPLoader(
cfg.HTTP.URL,
loader.TimeoutHTTPLoaderOption(cfg.HTTP.Timeout),
)))
}
return xadmission.NewAdmission(opts...)
}
func List(name string, names ...string) []admission.Admission {
var admissions []admission.Admission
if adm := registry.AdmissionRegistry().Get(name); adm != nil {
admissions = append(admissions, adm)
}
for _, s := range names {
if adm := registry.AdmissionRegistry().Get(s); adm != nil {
admissions = append(admissions, adm)
}
}
return admissions
}

View File

@ -0,0 +1,120 @@
package auth
import (
"crypto/tls"
"net/url"
"github.com/go-gost/core/auth"
"github.com/go-gost/core/logger"
xauth "github.com/go-gost/x/auth"
"github.com/go-gost/x/config"
"github.com/go-gost/x/internal/loader"
"github.com/go-gost/x/internal/plugin"
"github.com/go-gost/x/registry"
)
func ParseAuther(cfg *config.AutherConfig) auth.Authenticator {
if cfg == nil {
return nil
}
if cfg.Plugin != nil {
var tlsCfg *tls.Config
if cfg.Plugin.TLS != nil {
tlsCfg = &tls.Config{
ServerName: cfg.Plugin.TLS.ServerName,
InsecureSkipVerify: !cfg.Plugin.TLS.Secure,
}
}
switch cfg.Plugin.Type {
case "http":
return xauth.NewHTTPPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
)
default:
return xauth.NewGRPCPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),
)
}
}
m := make(map[string]string)
for _, user := range cfg.Auths {
if user.Username == "" {
continue
}
m[user.Username] = user.Password
}
opts := []xauth.Option{
xauth.AuthsOption(m),
xauth.ReloadPeriodOption(cfg.Reload),
xauth.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "auther",
"auther": cfg.Name,
})),
}
if cfg.File != nil && cfg.File.Path != "" {
opts = append(opts, xauth.FileLoaderOption(loader.FileLoader(cfg.File.Path)))
}
if cfg.Redis != nil && cfg.Redis.Addr != "" {
opts = append(opts, xauth.RedisLoaderOption(loader.RedisHashLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
}
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
opts = append(opts, xauth.HTTPLoaderOption(loader.HTTPLoader(
cfg.HTTP.URL,
loader.TimeoutHTTPLoaderOption(cfg.HTTP.Timeout),
)))
}
return xauth.NewAuthenticator(opts...)
}
func ParseAutherFromAuth(au *config.AuthConfig) auth.Authenticator {
if au == nil || au.Username == "" {
return nil
}
return xauth.NewAuthenticator(
xauth.AuthsOption(
map[string]string{
au.Username: au.Password,
},
),
xauth.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "auther",
})),
)
}
func Info(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 List(name string, names ...string) []auth.Authenticator {
var authers []auth.Authenticator
if auther := registry.AutherRegistry().Get(name); auther != nil {
authers = append(authers, auther)
}
for _, s := range names {
if auther := registry.AutherRegistry().Get(s); auther != nil {
authers = append(authers, auther)
}
}
return authers
}

View File

@ -0,0 +1,86 @@
package bypass
import (
"crypto/tls"
"strings"
"github.com/go-gost/core/bypass"
"github.com/go-gost/core/logger"
xbypass "github.com/go-gost/x/bypass"
"github.com/go-gost/x/config"
"github.com/go-gost/x/internal/loader"
"github.com/go-gost/x/internal/plugin"
"github.com/go-gost/x/registry"
)
func ParseBypass(cfg *config.BypassConfig) bypass.Bypass {
if cfg == nil {
return nil
}
if cfg.Plugin != nil {
var tlsCfg *tls.Config
if cfg.Plugin.TLS != nil {
tlsCfg = &tls.Config{
ServerName: cfg.Plugin.TLS.ServerName,
InsecureSkipVerify: !cfg.Plugin.TLS.Secure,
}
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return xbypass.NewHTTPPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
)
default:
return xbypass.NewGRPCPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),
)
}
}
opts := []xbypass.Option{
xbypass.MatchersOption(cfg.Matchers),
xbypass.WhitelistOption(cfg.Reverse || cfg.Whitelist),
xbypass.ReloadPeriodOption(cfg.Reload),
xbypass.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "bypass",
"bypass": cfg.Name,
})),
}
if cfg.File != nil && cfg.File.Path != "" {
opts = append(opts, xbypass.FileLoaderOption(loader.FileLoader(cfg.File.Path)))
}
if cfg.Redis != nil && cfg.Redis.Addr != "" {
opts = append(opts, xbypass.RedisLoaderOption(loader.RedisSetLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
}
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
opts = append(opts, xbypass.HTTPLoaderOption(loader.HTTPLoader(
cfg.HTTP.URL,
loader.TimeoutHTTPLoaderOption(cfg.HTTP.Timeout),
)))
}
return xbypass.NewBypass(opts...)
}
func List(name string, names ...string) []bypass.Bypass {
var bypasses []bypass.Bypass
if bp := registry.BypassRegistry().Get(name); bp != nil {
bypasses = append(bypasses, bp)
}
for _, s := range names {
if bp := registry.BypassRegistry().Get(s); bp != nil {
bypasses = append(bypasses, bp)
}
}
return bypasses
}

View File

@ -1,270 +0,0 @@
package parsing
import (
"fmt"
"net"
"strings"
"time"
"github.com/go-gost/core/bypass"
"github.com/go-gost/core/chain"
"github.com/go-gost/core/connector"
"github.com/go-gost/core/dialer"
"github.com/go-gost/core/logger"
"github.com/go-gost/core/metadata"
mdutil "github.com/go-gost/core/metadata/util"
auther "github.com/go-gost/x/auth"
xchain "github.com/go-gost/x/chain"
"github.com/go-gost/x/config"
tls_util "github.com/go-gost/x/internal/util/tls"
mdx "github.com/go-gost/x/metadata"
"github.com/go-gost/x/registry"
)
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,
})
var md metadata.Metadata
if cfg.Metadata != nil {
md = mdx.NewMetadata(cfg.Metadata)
}
c := xchain.NewChain(cfg.Name,
xchain.MetadataChainOption(md),
xchain.LoggerChainOption(chainLogger),
)
for _, ch := range cfg.Hops {
var hop chain.Hop
var err error
if len(ch.Nodes) > 0 {
if hop, err = ParseHop(ch); err != nil {
return nil, err
}
} else {
hop = registry.HopRegistry().Get(ch.Name)
}
if hop != nil {
c.AddHop(hop)
}
}
return c, nil
}
func ParseHop(cfg *config.HopConfig) (chain.Hop, error) {
if cfg == nil {
return nil, nil
}
hopLogger := logger.Default().WithFields(map[string]any{
"kind": "hop",
"hop": cfg.Name,
})
var nodes []*chain.Node
for _, v := range cfg.Nodes {
if v == nil {
continue
}
if v.Connector == nil {
v.Connector = &config.ConnectorConfig{
Type: "http",
}
}
if v.Dialer == nil {
v.Dialer = &config.DialerConfig{
Type: "tcp",
}
}
nodeLogger := hopLogger.WithFields(map[string]any{
"kind": "node",
"node": v.Name,
"connector": v.Connector.Type,
"dialer": v.Dialer.Type,
})
serverName, _, _ := net.SplitHostPort(v.Addr)
tlsCfg := v.Connector.TLS
if tlsCfg == nil {
tlsCfg = &config.TLSConfig{}
}
if tlsCfg.ServerName == "" {
tlsCfg.ServerName = serverName
}
tlsConfig, err := tls_util.LoadClientConfig(
tlsCfg.CertFile, tlsCfg.KeyFile, tlsCfg.CAFile,
tlsCfg.Secure, tlsCfg.ServerName)
if err != nil {
hopLogger.Error(err)
return nil, err
}
var nm metadata.Metadata
if v.Metadata != nil {
nm = mdx.NewMetadata(v.Metadata)
}
connectorLogger := nodeLogger.WithFields(map[string]any{
"kind": "connector",
})
var cr connector.Connector
if rf := registry.ConnectorRegistry().Get(v.Connector.Type); rf != nil {
cr = rf(
connector.AuthOption(parseAuth(v.Connector.Auth)),
connector.TLSConfigOption(tlsConfig),
connector.LoggerOption(connectorLogger),
)
} else {
return nil, fmt.Errorf("unregistered connector: %s", v.Connector.Type)
}
if v.Connector.Metadata == nil {
v.Connector.Metadata = make(map[string]any)
}
if err := cr.Init(mdx.NewMetadata(v.Connector.Metadata)); err != nil {
connectorLogger.Error("init: ", err)
return nil, err
}
tlsCfg = v.Dialer.TLS
if tlsCfg == nil {
tlsCfg = &config.TLSConfig{}
}
if tlsCfg.ServerName == "" {
tlsCfg.ServerName = serverName
}
tlsConfig, err = tls_util.LoadClientConfig(
tlsCfg.CertFile, tlsCfg.KeyFile, tlsCfg.CAFile,
tlsCfg.Secure, tlsCfg.ServerName)
if err != nil {
hopLogger.Error(err)
return nil, err
}
var ppv int
if nm != nil {
ppv = mdutil.GetInt(nm, mdKeyProxyProtocol)
}
dialerLogger := nodeLogger.WithFields(map[string]any{
"kind": "dialer",
})
var d dialer.Dialer
if rf := registry.DialerRegistry().Get(v.Dialer.Type); rf != nil {
d = rf(
dialer.AuthOption(parseAuth(v.Dialer.Auth)),
dialer.TLSConfigOption(tlsConfig),
dialer.LoggerOption(dialerLogger),
dialer.ProxyProtocolOption(ppv),
)
} else {
return nil, fmt.Errorf("unregistered dialer: %s", v.Dialer.Type)
}
if v.Dialer.Metadata == nil {
v.Dialer.Metadata = make(map[string]any)
}
if err := d.Init(mdx.NewMetadata(v.Dialer.Metadata)); err != nil {
dialerLogger.Error("init: ", err)
return nil, err
}
if v.Resolver == "" {
v.Resolver = cfg.Resolver
}
if v.Hosts == "" {
v.Hosts = cfg.Hosts
}
if v.Interface == "" {
v.Interface = cfg.Interface
}
if v.SockOpts == nil {
v.SockOpts = cfg.SockOpts
}
var sockOpts *chain.SockOpts
if v.SockOpts != nil {
sockOpts = &chain.SockOpts{
Mark: v.SockOpts.Mark,
}
}
tr := chain.NewTransport(d, cr,
chain.AddrTransportOption(v.Addr),
chain.InterfaceTransportOption(v.Interface),
chain.SockOptsTransportOption(sockOpts),
chain.TimeoutTransportOption(10*time.Second),
)
// convert *.example.com to .example.com
// convert *example.com to example.com
host := v.Host
if strings.HasPrefix(host, "*") {
host = host[1:]
if !strings.HasPrefix(host, ".") {
host = "." + host
}
}
opts := []chain.NodeOption{
chain.TransportNodeOption(tr),
chain.BypassNodeOption(bypass.BypassGroup(bypassList(v.Bypass, v.Bypasses...)...)),
chain.ResoloverNodeOption(registry.ResolverRegistry().Get(v.Resolver)),
chain.HostMapperNodeOption(registry.HostsRegistry().Get(v.Hosts)),
chain.MetadataNodeOption(nm),
chain.HostNodeOption(host),
chain.ProtocolNodeOption(v.Protocol),
}
if v.HTTP != nil {
opts = append(opts, chain.HTTPNodeOption(&chain.HTTPNodeSettings{
Host: v.HTTP.Host,
Header: v.HTTP.Header,
}))
}
if v.TLS != nil {
opts = append(opts, chain.TLSNodeOption(&chain.TLSNodeSettings{
ServerName: v.TLS.ServerName,
Secure: v.TLS.Secure,
}))
}
if v.Auth != nil {
opts = append(opts, chain.AutherNodeOption(
auther.NewAuthenticator(
auther.AuthsOption(map[string]string{v.Auth.Username: v.Auth.Password}),
auther.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "node",
"node": v.Name,
"addr": v.Addr,
"host": v.Host,
"protocol": v.Protocol,
})),
)))
}
node := chain.NewNode(v.Name, v.Addr, opts...)
nodes = append(nodes, node)
}
sel := parseNodeSelector(cfg.Selector)
if sel == nil {
sel = defaultNodeSelector()
}
return xchain.NewChainHop(nodes,
xchain.SelectorHopOption(sel),
xchain.BypassHopOption(bypass.BypassGroup(bypassList(cfg.Bypass, cfg.Bypasses...)...)),
xchain.LoggerHopOption(hopLogger),
), nil
}

View File

@ -0,0 +1,52 @@
package chain
import (
"github.com/go-gost/core/chain"
"github.com/go-gost/core/hop"
"github.com/go-gost/core/logger"
"github.com/go-gost/core/metadata"
xchain "github.com/go-gost/x/chain"
"github.com/go-gost/x/config"
hop_parser "github.com/go-gost/x/config/parsing/hop"
mdx "github.com/go-gost/x/metadata"
"github.com/go-gost/x/registry"
)
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,
})
var md metadata.Metadata
if cfg.Metadata != nil {
md = mdx.NewMetadata(cfg.Metadata)
}
c := xchain.NewChain(cfg.Name,
xchain.MetadataChainOption(md),
xchain.LoggerChainOption(chainLogger),
)
for _, ch := range cfg.Hops {
var hop hop.Hop
var err error
if ch.Nodes != nil || ch.Plugin != nil {
if hop, err = hop_parser.ParseHop(ch); err != nil {
return nil, err
}
} else {
hop = registry.HopRegistry().Get(ch.Name)
}
if hop != nil {
c.AddHop(hop)
}
}
return c, nil
}

124
config/parsing/hop/parse.go Normal file
View File

@ -0,0 +1,124 @@
package hop
import (
"crypto/tls"
"strings"
"github.com/go-gost/core/bypass"
"github.com/go-gost/core/chain"
"github.com/go-gost/core/hop"
"github.com/go-gost/core/logger"
"github.com/go-gost/x/config"
bypass_parser "github.com/go-gost/x/config/parsing/bypass"
node_parser "github.com/go-gost/x/config/parsing/node"
selector_parser "github.com/go-gost/x/config/parsing/selector"
xhop "github.com/go-gost/x/hop"
"github.com/go-gost/x/internal/loader"
"github.com/go-gost/x/internal/plugin"
)
func ParseHop(cfg *config.HopConfig) (hop.Hop, error) {
if cfg == nil {
return nil, nil
}
if cfg.Plugin != nil {
var tlsCfg *tls.Config
if cfg.Plugin.TLS != nil {
tlsCfg = &tls.Config{
ServerName: cfg.Plugin.TLS.ServerName,
InsecureSkipVerify: !cfg.Plugin.TLS.Secure,
}
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return xhop.NewHTTPPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
), nil
default:
return xhop.NewGRPCPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),
), nil
}
}
var nodes []*chain.Node
for _, v := range cfg.Nodes {
if v == nil {
continue
}
if v.Resolver == "" {
v.Resolver = cfg.Resolver
}
if v.Hosts == "" {
v.Hosts = cfg.Hosts
}
if v.Interface == "" {
v.Interface = cfg.Interface
}
if v.SockOpts == nil {
v.SockOpts = cfg.SockOpts
}
if v.Connector == nil {
v.Connector = &config.ConnectorConfig{
Type: "http",
}
}
if v.Dialer == nil {
v.Dialer = &config.DialerConfig{
Type: "tcp",
}
}
node, err := node_parser.ParseNode(cfg.Name, v)
if err != nil {
return nil, err
}
if node != nil {
nodes = append(nodes, node)
}
}
sel := selector_parser.ParseNodeSelector(cfg.Selector)
if sel == nil {
sel = selector_parser.DefaultNodeSelector()
}
opts := []xhop.Option{
xhop.NameOption(cfg.Name),
xhop.NodeOption(nodes...),
xhop.SelectorOption(sel),
xhop.BypassOption(bypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
xhop.ReloadPeriodOption(cfg.Reload),
xhop.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "hop",
"hop": cfg.Name,
})),
}
if cfg.File != nil && cfg.File.Path != "" {
opts = append(opts, xhop.FileLoaderOption(loader.FileLoader(cfg.File.Path)))
}
if cfg.Redis != nil && cfg.Redis.Addr != "" {
opts = append(opts, xhop.RedisLoaderOption(loader.RedisStringLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
}
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
opts = append(opts, xhop.HTTPLoaderOption(loader.HTTPLoader(
cfg.HTTP.URL,
loader.TimeoutHTTPLoaderOption(cfg.HTTP.Timeout),
)))
}
return xhop.NewHop(opts...), nil
}

View File

@ -0,0 +1,96 @@
package hosts
import (
"crypto/tls"
"net"
"strings"
"github.com/go-gost/core/hosts"
"github.com/go-gost/core/logger"
"github.com/go-gost/x/config"
xhosts "github.com/go-gost/x/hosts"
"github.com/go-gost/x/internal/loader"
"github.com/go-gost/x/internal/plugin"
)
func ParseHostMapper(cfg *config.HostsConfig) hosts.HostMapper {
if cfg == nil {
return nil
}
if cfg.Plugin != nil {
var tlsCfg *tls.Config
if cfg.Plugin.TLS != nil {
tlsCfg = &tls.Config{
ServerName: cfg.Plugin.TLS.ServerName,
InsecureSkipVerify: !cfg.Plugin.TLS.Secure,
}
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return xhosts.NewHTTPPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
)
default:
return xhosts.NewGRPCPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),
)
}
}
var mappings []xhosts.Mapping
for _, mapping := range cfg.Mappings {
if mapping.IP == "" || mapping.Hostname == "" {
continue
}
ip := net.ParseIP(mapping.IP)
if ip == nil {
continue
}
mappings = append(mappings, xhosts.Mapping{
Hostname: mapping.Hostname,
IP: ip,
})
}
opts := []xhosts.Option{
xhosts.MappingsOption(mappings),
xhosts.ReloadPeriodOption(cfg.Reload),
xhosts.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "hosts",
"hosts": cfg.Name,
})),
}
if cfg.File != nil && cfg.File.Path != "" {
opts = append(opts, xhosts.FileLoaderOption(loader.FileLoader(cfg.File.Path)))
}
if cfg.Redis != nil && cfg.Redis.Addr != "" {
switch cfg.Redis.Type {
case "list": // redis list
opts = append(opts, xhosts.RedisLoaderOption(loader.RedisListLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
default: // redis set
opts = append(opts, xhosts.RedisLoaderOption(loader.RedisSetLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
}
}
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
opts = append(opts, xhosts.HTTPLoaderOption(loader.HTTPLoader(
cfg.HTTP.URL,
loader.TimeoutHTTPLoaderOption(cfg.HTTP.Timeout),
)))
}
return xhosts.NewHostMapper(opts...)
}

View File

@ -0,0 +1,91 @@
package ingress
import (
"crypto/tls"
"strings"
"github.com/go-gost/core/ingress"
"github.com/go-gost/core/logger"
"github.com/go-gost/x/config"
xingress "github.com/go-gost/x/ingress"
"github.com/go-gost/x/internal/loader"
"github.com/go-gost/x/internal/plugin"
)
func ParseIngress(cfg *config.IngressConfig) ingress.Ingress {
if cfg == nil {
return nil
}
if cfg.Plugin != nil {
var tlsCfg *tls.Config
if cfg.Plugin.TLS != nil {
tlsCfg = &tls.Config{
ServerName: cfg.Plugin.TLS.ServerName,
InsecureSkipVerify: !cfg.Plugin.TLS.Secure,
}
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return xingress.NewHTTPPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
)
default:
return xingress.NewGRPCPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),
)
}
}
var rules []xingress.Rule
for _, rule := range cfg.Rules {
if rule.Hostname == "" || rule.Endpoint == "" {
continue
}
rules = append(rules, xingress.Rule{
Hostname: rule.Hostname,
Endpoint: rule.Endpoint,
})
}
opts := []xingress.Option{
xingress.RulesOption(rules),
xingress.ReloadPeriodOption(cfg.Reload),
xingress.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "ingress",
"ingress": cfg.Name,
})),
}
if cfg.File != nil && cfg.File.Path != "" {
opts = append(opts, xingress.FileLoaderOption(loader.FileLoader(cfg.File.Path)))
}
if cfg.Redis != nil && cfg.Redis.Addr != "" {
switch cfg.Redis.Type {
case "set": // redis set
opts = append(opts, xingress.RedisLoaderOption(loader.RedisSetLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
default: // redis hash
opts = append(opts, xingress.RedisLoaderOption(loader.RedisHashLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
}
}
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
opts = append(opts, xingress.HTTPLoaderOption(loader.HTTPLoader(
cfg.HTTP.URL,
loader.TimeoutHTTPLoaderOption(cfg.HTTP.Timeout),
)))
}
return xingress.NewIngress(opts...)
}

View File

@ -0,0 +1,151 @@
package limiter
import (
"github.com/go-gost/core/limiter/conn"
"github.com/go-gost/core/limiter/rate"
"github.com/go-gost/core/limiter/traffic"
"github.com/go-gost/core/logger"
"github.com/go-gost/x/config"
"github.com/go-gost/x/internal/loader"
xconn "github.com/go-gost/x/limiter/conn"
xrate "github.com/go-gost/x/limiter/rate"
xtraffic "github.com/go-gost/x/limiter/traffic"
)
func ParseTrafficLimiter(cfg *config.LimiterConfig) (lim traffic.TrafficLimiter) {
if cfg == nil {
return nil
}
var opts []xtraffic.Option
if cfg.File != nil && cfg.File.Path != "" {
opts = append(opts, xtraffic.FileLoaderOption(loader.FileLoader(cfg.File.Path)))
}
if cfg.Redis != nil && cfg.Redis.Addr != "" {
switch cfg.Redis.Type {
case "list": // redis list
opts = append(opts, xtraffic.RedisLoaderOption(loader.RedisListLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
default: // redis set
opts = append(opts, xtraffic.RedisLoaderOption(loader.RedisSetLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
}
}
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
opts = append(opts, xtraffic.HTTPLoaderOption(loader.HTTPLoader(
cfg.HTTP.URL,
loader.TimeoutHTTPLoaderOption(cfg.HTTP.Timeout),
)))
}
opts = append(opts,
xtraffic.LimitsOption(cfg.Limits...),
xtraffic.ReloadPeriodOption(cfg.Reload),
xtraffic.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "limiter",
"limiter": cfg.Name,
})),
)
return xtraffic.NewTrafficLimiter(opts...)
}
func ParseConnLimiter(cfg *config.LimiterConfig) (lim conn.ConnLimiter) {
if cfg == nil {
return nil
}
var opts []xconn.Option
if cfg.File != nil && cfg.File.Path != "" {
opts = append(opts, xconn.FileLoaderOption(loader.FileLoader(cfg.File.Path)))
}
if cfg.Redis != nil && cfg.Redis.Addr != "" {
switch cfg.Redis.Type {
case "list": // redis list
opts = append(opts, xconn.RedisLoaderOption(loader.RedisListLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
default: // redis set
opts = append(opts, xconn.RedisLoaderOption(loader.RedisSetLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
}
}
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
opts = append(opts, xconn.HTTPLoaderOption(loader.HTTPLoader(
cfg.HTTP.URL,
loader.TimeoutHTTPLoaderOption(cfg.HTTP.Timeout),
)))
}
opts = append(opts,
xconn.LimitsOption(cfg.Limits...),
xconn.ReloadPeriodOption(cfg.Reload),
xconn.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "limiter",
"limiter": cfg.Name,
})),
)
return xconn.NewConnLimiter(opts...)
}
func ParseRateLimiter(cfg *config.LimiterConfig) (lim rate.RateLimiter) {
if cfg == nil {
return nil
}
var opts []xrate.Option
if cfg.File != nil && cfg.File.Path != "" {
opts = append(opts, xrate.FileLoaderOption(loader.FileLoader(cfg.File.Path)))
}
if cfg.Redis != nil && cfg.Redis.Addr != "" {
switch cfg.Redis.Type {
case "list": // redis list
opts = append(opts, xrate.RedisLoaderOption(loader.RedisListLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
default: // redis set
opts = append(opts, xrate.RedisLoaderOption(loader.RedisSetLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
}
}
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
opts = append(opts, xrate.HTTPLoaderOption(loader.HTTPLoader(
cfg.HTTP.URL,
loader.TimeoutHTTPLoaderOption(cfg.HTTP.Timeout),
)))
}
opts = append(opts,
xrate.LimitsOption(cfg.Limits...),
xrate.ReloadPeriodOption(cfg.Reload),
xrate.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "limiter",
"limiter": cfg.Name,
})),
)
return xrate.NewRateLimiter(opts...)
}

View File

@ -0,0 +1,198 @@
package node
import (
"fmt"
"net"
"strings"
"time"
"github.com/go-gost/core/bypass"
"github.com/go-gost/core/chain"
"github.com/go-gost/core/connector"
"github.com/go-gost/core/dialer"
"github.com/go-gost/core/logger"
"github.com/go-gost/core/metadata"
mdutil "github.com/go-gost/core/metadata/util"
xauth "github.com/go-gost/x/auth"
"github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing"
auth_parser "github.com/go-gost/x/config/parsing/auth"
bypass_parser "github.com/go-gost/x/config/parsing/bypass"
tls_util "github.com/go-gost/x/internal/util/tls"
mdx "github.com/go-gost/x/metadata"
"github.com/go-gost/x/registry"
)
func ParseNode(hop string, cfg *config.NodeConfig) (*chain.Node, error) {
if cfg == nil {
return nil, nil
}
if cfg.Connector == nil {
cfg.Connector = &config.ConnectorConfig{
Type: "http",
}
}
if cfg.Dialer == nil {
cfg.Dialer = &config.DialerConfig{
Type: "tcp",
}
}
nodeLogger := logger.Default().WithFields(map[string]any{
"hop": hop,
"kind": "node",
"node": cfg.Name,
"connector": cfg.Connector.Type,
"dialer": cfg.Dialer.Type,
})
serverName, _, _ := net.SplitHostPort(cfg.Addr)
tlsCfg := cfg.Connector.TLS
if tlsCfg == nil {
tlsCfg = &config.TLSConfig{}
}
if tlsCfg.ServerName == "" {
tlsCfg.ServerName = serverName
}
tlsConfig, err := tls_util.LoadClientConfig(
tlsCfg.CertFile, tlsCfg.KeyFile, tlsCfg.CAFile,
tlsCfg.Secure, tlsCfg.ServerName)
if err != nil {
nodeLogger.Error(err)
return nil, err
}
var nm metadata.Metadata
if cfg.Metadata != nil {
nm = mdx.NewMetadata(cfg.Metadata)
}
connectorLogger := nodeLogger.WithFields(map[string]any{
"kind": "connector",
})
var cr connector.Connector
if rf := registry.ConnectorRegistry().Get(cfg.Connector.Type); rf != nil {
cr = rf(
connector.AuthOption(auth_parser.Info(cfg.Connector.Auth)),
connector.TLSConfigOption(tlsConfig),
connector.LoggerOption(connectorLogger),
)
} else {
return nil, fmt.Errorf("unregistered connector: %s", cfg.Connector.Type)
}
if cfg.Connector.Metadata == nil {
cfg.Connector.Metadata = make(map[string]any)
}
if err := cr.Init(mdx.NewMetadata(cfg.Connector.Metadata)); err != nil {
connectorLogger.Error("init: ", err)
return nil, err
}
tlsCfg = cfg.Dialer.TLS
if tlsCfg == nil {
tlsCfg = &config.TLSConfig{}
}
if tlsCfg.ServerName == "" {
tlsCfg.ServerName = serverName
}
tlsConfig, err = tls_util.LoadClientConfig(
tlsCfg.CertFile, tlsCfg.KeyFile, tlsCfg.CAFile,
tlsCfg.Secure, tlsCfg.ServerName)
if err != nil {
nodeLogger.Error(err)
return nil, err
}
var ppv int
if nm != nil {
ppv = mdutil.GetInt(nm, parsing.MDKeyProxyProtocol)
}
dialerLogger := nodeLogger.WithFields(map[string]any{
"kind": "dialer",
})
var d dialer.Dialer
if rf := registry.DialerRegistry().Get(cfg.Dialer.Type); rf != nil {
d = rf(
dialer.AuthOption(auth_parser.Info(cfg.Dialer.Auth)),
dialer.TLSConfigOption(tlsConfig),
dialer.LoggerOption(dialerLogger),
dialer.ProxyProtocolOption(ppv),
)
} else {
return nil, fmt.Errorf("unregistered dialer: %s", cfg.Dialer.Type)
}
if cfg.Dialer.Metadata == nil {
cfg.Dialer.Metadata = make(map[string]any)
}
if err := d.Init(mdx.NewMetadata(cfg.Dialer.Metadata)); err != nil {
dialerLogger.Error("init: ", err)
return nil, err
}
var sockOpts *chain.SockOpts
if cfg.SockOpts != nil {
sockOpts = &chain.SockOpts{
Mark: cfg.SockOpts.Mark,
}
}
tr := chain.NewTransport(d, cr,
chain.AddrTransportOption(cfg.Addr),
chain.InterfaceTransportOption(cfg.Interface),
chain.SockOptsTransportOption(sockOpts),
chain.TimeoutTransportOption(10*time.Second),
)
// convert *.example.com to .example.com
// convert *example.com to example.com
host := cfg.Host
if strings.HasPrefix(host, "*") {
host = host[1:]
if !strings.HasPrefix(host, ".") {
host = "." + host
}
}
opts := []chain.NodeOption{
chain.TransportNodeOption(tr),
chain.BypassNodeOption(bypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
chain.ResoloverNodeOption(registry.ResolverRegistry().Get(cfg.Resolver)),
chain.HostMapperNodeOption(registry.HostsRegistry().Get(cfg.Hosts)),
chain.MetadataNodeOption(nm),
chain.HostNodeOption(host),
chain.ProtocolNodeOption(cfg.Protocol),
}
if cfg.HTTP != nil {
opts = append(opts, chain.HTTPNodeOption(&chain.HTTPNodeSettings{
Host: cfg.HTTP.Host,
Header: cfg.HTTP.Header,
}))
}
if cfg.TLS != nil {
opts = append(opts, chain.TLSNodeOption(&chain.TLSNodeSettings{
ServerName: cfg.TLS.ServerName,
Secure: cfg.TLS.Secure,
}))
}
if cfg.Auth != nil {
opts = append(opts, chain.AutherNodeOption(
xauth.NewAuthenticator(
xauth.AuthsOption(map[string]string{cfg.Auth.Username: cfg.Auth.Password}),
xauth.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "node",
"node": cfg.Name,
"addr": cfg.Addr,
"host": cfg.Host,
"protocol": cfg.Protocol,
})),
)))
}
return chain.NewNode(cfg.Name, cfg.Addr, opts...), nil
}

View File

@ -1,820 +1,17 @@
package parsing package parsing
import (
"context"
"crypto/tls"
"net"
"net/http"
"net/url"
"strings"
"github.com/go-gost/core/admission"
"github.com/go-gost/core/auth"
"github.com/go-gost/core/bypass"
"github.com/go-gost/core/chain"
"github.com/go-gost/core/hosts"
"github.com/go-gost/core/ingress"
"github.com/go-gost/core/limiter/conn"
"github.com/go-gost/core/limiter/rate"
"github.com/go-gost/core/limiter/traffic"
"github.com/go-gost/core/logger"
"github.com/go-gost/core/recorder"
"github.com/go-gost/core/resolver"
"github.com/go-gost/core/selector"
admission_impl "github.com/go-gost/x/admission"
auth_impl "github.com/go-gost/x/auth"
bypass_impl "github.com/go-gost/x/bypass"
"github.com/go-gost/x/config"
xhosts "github.com/go-gost/x/hosts"
xingress "github.com/go-gost/x/ingress"
"github.com/go-gost/x/internal/loader"
"github.com/go-gost/x/internal/util/plugin"
xconn "github.com/go-gost/x/limiter/conn"
xrate "github.com/go-gost/x/limiter/rate"
xtraffic "github.com/go-gost/x/limiter/traffic"
xrecorder "github.com/go-gost/x/recorder"
"github.com/go-gost/x/registry"
resolver_impl "github.com/go-gost/x/resolver"
xs "github.com/go-gost/x/selector"
"google.golang.org/grpc"
"google.golang.org/grpc/backoff"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
)
const ( const (
mdKeyProxyProtocol = "proxyProtocol" MDKeyProxyProtocol = "proxyProtocol"
mdKeyInterface = "interface" MDKeyInterface = "interface"
mdKeySoMark = "so_mark" MDKeySoMark = "so_mark"
mdKeyHash = "hash" MDKeyHash = "hash"
mdKeyPreUp = "preUp" MDKeyPreUp = "preUp"
mdKeyPreDown = "preDown" MDKeyPreDown = "preDown"
mdKeyPostUp = "postUp" MDKeyPostUp = "postUp"
mdKeyPostDown = "postDown" MDKeyPostDown = "postDown"
mdKeyIgnoreChain = "ignoreChain" MDKeyIgnoreChain = "ignoreChain"
mdKeyRecorderDirection = "direction" MDKeyRecorderDirection = "direction"
mdKeyRecorderTimestampFormat = "timeStampFormat" MDKeyRecorderTimestampFormat = "timeStampFormat"
mdKeyRecorderHexdump = "hexdump" MDKeyRecorderHexdump = "hexdump"
) )
func ParseAuther(cfg *config.AutherConfig) auth.Authenticator {
if cfg == nil {
return nil
}
if cfg.Plugin != nil {
var tlsCfg *tls.Config
if cfg.Plugin.TLS != nil {
tlsCfg = &tls.Config{
ServerName: cfg.Plugin.TLS.ServerName,
InsecureSkipVerify: !cfg.Plugin.TLS.Secure,
}
}
switch cfg.Plugin.Type {
case "http":
return auth_impl.NewHTTPPluginAuthenticator(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
)
default:
return auth_impl.NewGRPCPluginAuthenticator(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),
)
}
}
m := make(map[string]string)
for _, user := range cfg.Auths {
if user.Username == "" {
continue
}
m[user.Username] = user.Password
}
opts := []auth_impl.Option{
auth_impl.AuthsOption(m),
auth_impl.ReloadPeriodOption(cfg.Reload),
auth_impl.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "auther",
"auther": cfg.Name,
})),
}
if cfg.File != nil && cfg.File.Path != "" {
opts = append(opts, auth_impl.FileLoaderOption(loader.FileLoader(cfg.File.Path)))
}
if cfg.Redis != nil && cfg.Redis.Addr != "" {
opts = append(opts, auth_impl.RedisLoaderOption(loader.RedisHashLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
}
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
opts = append(opts, auth_impl.HTTPLoaderOption(loader.HTTPLoader(
cfg.HTTP.URL,
loader.TimeoutHTTPLoaderOption(cfg.HTTP.Timeout),
)))
}
return auth_impl.NewAuthenticator(opts...)
}
func ParseAutherFromAuth(au *config.AuthConfig) auth.Authenticator {
if au == nil || au.Username == "" {
return nil
}
return auth_impl.NewAuthenticator(
auth_impl.AuthsOption(
map[string]string{
au.Username: au.Password,
},
),
auth_impl.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "auther",
})),
)
}
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 parseChainSelector(cfg *config.SelectorConfig) selector.Selector[chain.Chainer] {
if cfg == nil {
return nil
}
var strategy selector.Strategy[chain.Chainer]
switch cfg.Strategy {
case "round", "rr":
strategy = xs.RoundRobinStrategy[chain.Chainer]()
case "random", "rand":
strategy = xs.RandomStrategy[chain.Chainer]()
case "fifo", "ha":
strategy = xs.FIFOStrategy[chain.Chainer]()
case "hash":
strategy = xs.HashStrategy[chain.Chainer]()
default:
strategy = xs.RoundRobinStrategy[chain.Chainer]()
}
return xs.NewSelector(
strategy,
xs.FailFilter[chain.Chainer](cfg.MaxFails, cfg.FailTimeout),
xs.BackupFilter[chain.Chainer](),
)
}
func parseNodeSelector(cfg *config.SelectorConfig) selector.Selector[*chain.Node] {
if cfg == nil {
return nil
}
var strategy selector.Strategy[*chain.Node]
switch cfg.Strategy {
case "round", "rr":
strategy = xs.RoundRobinStrategy[*chain.Node]()
case "random", "rand":
strategy = xs.RandomStrategy[*chain.Node]()
case "fifo", "ha":
strategy = xs.FIFOStrategy[*chain.Node]()
case "hash":
strategy = xs.HashStrategy[*chain.Node]()
default:
strategy = xs.RoundRobinStrategy[*chain.Node]()
}
return xs.NewSelector(
strategy,
xs.FailFilter[*chain.Node](cfg.MaxFails, cfg.FailTimeout),
xs.BackupFilter[*chain.Node](),
)
}
func ParseAdmission(cfg *config.AdmissionConfig) admission.Admission {
if cfg == nil {
return nil
}
if cfg.Plugin != nil {
var tlsCfg *tls.Config
if cfg.Plugin.TLS != nil {
tlsCfg = &tls.Config{
ServerName: cfg.Plugin.TLS.ServerName,
InsecureSkipVerify: !cfg.Plugin.TLS.Secure,
}
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return admission_impl.NewHTTPPluginAdmission(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
)
default:
return admission_impl.NewGRPCPluginAdmission(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),
)
}
}
opts := []admission_impl.Option{
admission_impl.MatchersOption(cfg.Matchers),
admission_impl.WhitelistOption(cfg.Reverse || cfg.Whitelist),
admission_impl.ReloadPeriodOption(cfg.Reload),
admission_impl.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "admission",
"admission": cfg.Name,
})),
}
if cfg.File != nil && cfg.File.Path != "" {
opts = append(opts, admission_impl.FileLoaderOption(loader.FileLoader(cfg.File.Path)))
}
if cfg.Redis != nil && cfg.Redis.Addr != "" {
opts = append(opts, admission_impl.RedisLoaderOption(loader.RedisSetLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
}
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
opts = append(opts, admission_impl.HTTPLoaderOption(loader.HTTPLoader(
cfg.HTTP.URL,
loader.TimeoutHTTPLoaderOption(cfg.HTTP.Timeout),
)))
}
return admission_impl.NewAdmission(opts...)
}
func ParseBypass(cfg *config.BypassConfig) bypass.Bypass {
if cfg == nil {
return nil
}
if cfg.Plugin != nil {
var tlsCfg *tls.Config
if cfg.Plugin.TLS != nil {
tlsCfg = &tls.Config{
ServerName: cfg.Plugin.TLS.ServerName,
InsecureSkipVerify: !cfg.Plugin.TLS.Secure,
}
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return bypass_impl.NewHTTPPluginBypass(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
)
default:
return bypass_impl.NewGRPCPluginBypass(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),
)
}
}
opts := []bypass_impl.Option{
bypass_impl.MatchersOption(cfg.Matchers),
bypass_impl.WhitelistOption(cfg.Reverse || cfg.Whitelist),
bypass_impl.ReloadPeriodOption(cfg.Reload),
bypass_impl.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "bypass",
"bypass": cfg.Name,
})),
}
if cfg.File != nil && cfg.File.Path != "" {
opts = append(opts, bypass_impl.FileLoaderOption(loader.FileLoader(cfg.File.Path)))
}
if cfg.Redis != nil && cfg.Redis.Addr != "" {
opts = append(opts, bypass_impl.RedisLoaderOption(loader.RedisSetLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
}
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
opts = append(opts, bypass_impl.HTTPLoaderOption(loader.HTTPLoader(
cfg.HTTP.URL,
loader.TimeoutHTTPLoaderOption(cfg.HTTP.Timeout),
)))
}
return bypass_impl.NewBypass(opts...)
}
func ParseResolver(cfg *config.ResolverConfig) (resolver.Resolver, error) {
if cfg == nil {
return nil, nil
}
if cfg.Plugin != nil {
var tlsCfg *tls.Config
if cfg.Plugin.TLS != nil {
tlsCfg = &tls.Config{
ServerName: cfg.Plugin.TLS.ServerName,
InsecureSkipVerify: !cfg.Plugin.TLS.Secure,
}
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return resolver_impl.NewHTTPPluginResolver(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
), nil
default:
return resolver_impl.NewGRPCPluginResolver(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),
)
}
}
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.LoggerOption(
logger.Default().WithFields(map[string]any{
"kind": "resolver",
"resolver": cfg.Name,
}),
),
)
}
func ParseHosts(cfg *config.HostsConfig) hosts.HostMapper {
if cfg == nil {
return nil
}
if cfg.Plugin != nil {
var tlsCfg *tls.Config
if cfg.Plugin.TLS != nil {
tlsCfg = &tls.Config{
ServerName: cfg.Plugin.TLS.ServerName,
InsecureSkipVerify: !cfg.Plugin.TLS.Secure,
}
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return xhosts.NewHTTPPluginHostMapper(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
)
default:
return xhosts.NewGRPCPluginHostMapper(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),
)
}
}
var mappings []xhosts.Mapping
for _, mapping := range cfg.Mappings {
if mapping.IP == "" || mapping.Hostname == "" {
continue
}
ip := net.ParseIP(mapping.IP)
if ip == nil {
continue
}
mappings = append(mappings, xhosts.Mapping{
Hostname: mapping.Hostname,
IP: ip,
})
}
opts := []xhosts.Option{
xhosts.MappingsOption(mappings),
xhosts.ReloadPeriodOption(cfg.Reload),
xhosts.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "hosts",
"hosts": cfg.Name,
})),
}
if cfg.File != nil && cfg.File.Path != "" {
opts = append(opts, xhosts.FileLoaderOption(loader.FileLoader(cfg.File.Path)))
}
if cfg.Redis != nil && cfg.Redis.Addr != "" {
switch cfg.Redis.Type {
case "list": // redis list
opts = append(opts, xhosts.RedisLoaderOption(loader.RedisListLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
default: // redis set
opts = append(opts, xhosts.RedisLoaderOption(loader.RedisSetLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
}
}
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
opts = append(opts, xhosts.HTTPLoaderOption(loader.HTTPLoader(
cfg.HTTP.URL,
loader.TimeoutHTTPLoaderOption(cfg.HTTP.Timeout),
)))
}
return xhosts.NewHostMapper(opts...)
}
func ParseIngress(cfg *config.IngressConfig) ingress.Ingress {
if cfg == nil {
return nil
}
if cfg.Plugin != nil {
var tlsCfg *tls.Config
if cfg.Plugin.TLS != nil {
tlsCfg = &tls.Config{
ServerName: cfg.Plugin.TLS.ServerName,
InsecureSkipVerify: !cfg.Plugin.TLS.Secure,
}
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return xingress.NewHTTPPluginIngress(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
)
default:
return xingress.NewGRPCPluginIngress(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),
)
}
}
var rules []xingress.Rule
for _, rule := range cfg.Rules {
if rule.Hostname == "" || rule.Endpoint == "" {
continue
}
rules = append(rules, xingress.Rule{
Hostname: rule.Hostname,
Endpoint: rule.Endpoint,
})
}
opts := []xingress.Option{
xingress.RulesOption(rules),
xingress.ReloadPeriodOption(cfg.Reload),
xingress.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "ingress",
"ingress": cfg.Name,
})),
}
if cfg.File != nil && cfg.File.Path != "" {
opts = append(opts, xingress.FileLoaderOption(loader.FileLoader(cfg.File.Path)))
}
if cfg.Redis != nil && cfg.Redis.Addr != "" {
switch cfg.Redis.Type {
case "set": // redis set
opts = append(opts, xingress.RedisLoaderOption(loader.RedisSetLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
default: // redis hash
opts = append(opts, xingress.RedisLoaderOption(loader.RedisHashLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
}
}
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
opts = append(opts, xingress.HTTPLoaderOption(loader.HTTPLoader(
cfg.HTTP.URL,
loader.TimeoutHTTPLoaderOption(cfg.HTTP.Timeout),
)))
}
return xingress.NewIngress(opts...)
}
func ParseRecorder(cfg *config.RecorderConfig) (r recorder.Recorder) {
if cfg == nil {
return nil
}
if cfg.Plugin != nil {
var tlsCfg *tls.Config
if cfg.Plugin.TLS != nil {
tlsCfg = &tls.Config{
ServerName: cfg.Plugin.TLS.ServerName,
InsecureSkipVerify: !cfg.Plugin.TLS.Secure,
}
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return xrecorder.NewHTTPPluginRecorder(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
)
default:
return xrecorder.NewGRPCPluginRecorder(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),
)
}
}
if cfg.File != nil && cfg.File.Path != "" {
return xrecorder.FileRecorder(cfg.File.Path,
xrecorder.SepRecorderOption(cfg.File.Sep),
)
}
if cfg.TCP != nil && cfg.TCP.Addr != "" {
return xrecorder.TCPRecorder(cfg.TCP.Addr, xrecorder.TimeoutTCPRecorderOption(cfg.TCP.Timeout))
}
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
return xrecorder.HTTPRecorder(cfg.HTTP.URL, xrecorder.TimeoutHTTPRecorderOption(cfg.HTTP.Timeout))
}
if cfg.Redis != nil &&
cfg.Redis.Addr != "" &&
cfg.Redis.Key != "" {
switch cfg.Redis.Type {
case "list": // redis list
return xrecorder.RedisListRecorder(cfg.Redis.Addr,
xrecorder.DBRedisRecorderOption(cfg.Redis.DB),
xrecorder.KeyRedisRecorderOption(cfg.Redis.Key),
xrecorder.PasswordRedisRecorderOption(cfg.Redis.Password),
)
case "sset": // sorted set
return xrecorder.RedisSortedSetRecorder(cfg.Redis.Addr,
xrecorder.DBRedisRecorderOption(cfg.Redis.DB),
xrecorder.KeyRedisRecorderOption(cfg.Redis.Key),
xrecorder.PasswordRedisRecorderOption(cfg.Redis.Password),
)
default: // redis set
return xrecorder.RedisSetRecorder(cfg.Redis.Addr,
xrecorder.DBRedisRecorderOption(cfg.Redis.DB),
xrecorder.KeyRedisRecorderOption(cfg.Redis.Key),
xrecorder.PasswordRedisRecorderOption(cfg.Redis.Password),
)
}
}
return
}
func defaultNodeSelector() selector.Selector[*chain.Node] {
return xs.NewSelector(
xs.RoundRobinStrategy[*chain.Node](),
xs.FailFilter[*chain.Node](xs.DefaultMaxFails, xs.DefaultFailTimeout),
xs.BackupFilter[*chain.Node](),
)
}
func defaultChainSelector() selector.Selector[chain.Chainer] {
return xs.NewSelector(
xs.RoundRobinStrategy[chain.Chainer](),
xs.FailFilter[chain.Chainer](xs.DefaultMaxFails, xs.DefaultFailTimeout),
xs.BackupFilter[chain.Chainer](),
)
}
func ParseTrafficLimiter(cfg *config.LimiterConfig) (lim traffic.TrafficLimiter) {
if cfg == nil {
return nil
}
var opts []xtraffic.Option
if cfg.File != nil && cfg.File.Path != "" {
opts = append(opts, xtraffic.FileLoaderOption(loader.FileLoader(cfg.File.Path)))
}
if cfg.Redis != nil && cfg.Redis.Addr != "" {
switch cfg.Redis.Type {
case "list": // redis list
opts = append(opts, xtraffic.RedisLoaderOption(loader.RedisListLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
default: // redis set
opts = append(opts, xtraffic.RedisLoaderOption(loader.RedisSetLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
}
}
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
opts = append(opts, xtraffic.HTTPLoaderOption(loader.HTTPLoader(
cfg.HTTP.URL,
loader.TimeoutHTTPLoaderOption(cfg.HTTP.Timeout),
)))
}
opts = append(opts,
xtraffic.LimitsOption(cfg.Limits...),
xtraffic.ReloadPeriodOption(cfg.Reload),
xtraffic.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "limiter",
"limiter": cfg.Name,
})),
)
return xtraffic.NewTrafficLimiter(opts...)
}
func ParseConnLimiter(cfg *config.LimiterConfig) (lim conn.ConnLimiter) {
if cfg == nil {
return nil
}
var opts []xconn.Option
if cfg.File != nil && cfg.File.Path != "" {
opts = append(opts, xconn.FileLoaderOption(loader.FileLoader(cfg.File.Path)))
}
if cfg.Redis != nil && cfg.Redis.Addr != "" {
switch cfg.Redis.Type {
case "list": // redis list
opts = append(opts, xconn.RedisLoaderOption(loader.RedisListLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
default: // redis set
opts = append(opts, xconn.RedisLoaderOption(loader.RedisSetLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
}
}
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
opts = append(opts, xconn.HTTPLoaderOption(loader.HTTPLoader(
cfg.HTTP.URL,
loader.TimeoutHTTPLoaderOption(cfg.HTTP.Timeout),
)))
}
opts = append(opts,
xconn.LimitsOption(cfg.Limits...),
xconn.ReloadPeriodOption(cfg.Reload),
xconn.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "limiter",
"limiter": cfg.Name,
})),
)
return xconn.NewConnLimiter(opts...)
}
func ParseRateLimiter(cfg *config.LimiterConfig) (lim rate.RateLimiter) {
if cfg == nil {
return nil
}
var opts []xrate.Option
if cfg.File != nil && cfg.File.Path != "" {
opts = append(opts, xrate.FileLoaderOption(loader.FileLoader(cfg.File.Path)))
}
if cfg.Redis != nil && cfg.Redis.Addr != "" {
switch cfg.Redis.Type {
case "list": // redis list
opts = append(opts, xrate.RedisLoaderOption(loader.RedisListLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
default: // redis set
opts = append(opts, xrate.RedisLoaderOption(loader.RedisSetLoader(
cfg.Redis.Addr,
loader.DBRedisLoaderOption(cfg.Redis.DB),
loader.PasswordRedisLoaderOption(cfg.Redis.Password),
loader.KeyRedisLoaderOption(cfg.Redis.Key),
)))
}
}
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
opts = append(opts, xrate.HTTPLoaderOption(loader.HTTPLoader(
cfg.HTTP.URL,
loader.TimeoutHTTPLoaderOption(cfg.HTTP.Timeout),
)))
}
opts = append(opts,
xrate.LimitsOption(cfg.Limits...),
xrate.ReloadPeriodOption(cfg.Reload),
xrate.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "limiter",
"limiter": cfg.Name,
})),
)
return xrate.NewRateLimiter(opts...)
}
func newGRPCPluginConn(cfg *config.PluginConfig) (*grpc.ClientConn, error) {
grpcOpts := []grpc.DialOption{
// grpc.WithBlock(),
grpc.WithConnectParams(grpc.ConnectParams{
Backoff: backoff.DefaultConfig,
}),
grpc.FailOnNonTempDialError(true),
}
if tlsCfg := cfg.TLS; tlsCfg != nil && tlsCfg.Secure {
grpcOpts = append(grpcOpts,
grpc.WithAuthority(tlsCfg.ServerName),
grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{
ServerName: tlsCfg.ServerName,
InsecureSkipVerify: !tlsCfg.Secure,
})))
} else {
grpcOpts = append(grpcOpts, grpc.WithTransportCredentials(insecure.NewCredentials()))
}
if cfg.Token != "" {
grpcOpts = append(grpcOpts, grpc.WithPerRPCCredentials(&rpcCredentials{token: cfg.Token}))
}
return grpc.Dial(cfg.Addr, grpcOpts...)
}
type rpcCredentials struct {
token string
}
func (c *rpcCredentials) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
return map[string]string{
"token": c.token,
}, nil
}
func (c *rpcCredentials) RequireTransportSecurity() bool {
return false
}
func newHTTPPluginClient(cfg *config.PluginConfig) *http.Client {
if cfg == nil {
return nil
}
tr := &http.Transport{}
if cfg.TLS != nil {
if cfg.TLS.Secure {
tr.TLSClientConfig = &tls.Config{
ServerName: cfg.TLS.ServerName,
}
} else {
tr.TLSClientConfig = &tls.Config{
InsecureSkipVerify: true,
}
}
}
return &http.Client{
Timeout: cfg.Timeout,
Transport: tr,
}
}

View File

@ -0,0 +1,82 @@
package recorder
import (
"crypto/tls"
"strings"
"github.com/go-gost/core/recorder"
"github.com/go-gost/x/config"
"github.com/go-gost/x/internal/plugin"
xrecorder "github.com/go-gost/x/recorder"
)
func ParseRecorder(cfg *config.RecorderConfig) (r recorder.Recorder) {
if cfg == nil {
return nil
}
if cfg.Plugin != nil {
var tlsCfg *tls.Config
if cfg.Plugin.TLS != nil {
tlsCfg = &tls.Config{
ServerName: cfg.Plugin.TLS.ServerName,
InsecureSkipVerify: !cfg.Plugin.TLS.Secure,
}
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return xrecorder.NewHTTPPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
)
default:
return xrecorder.NewGRPCPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),
)
}
}
if cfg.File != nil && cfg.File.Path != "" {
return xrecorder.FileRecorder(cfg.File.Path,
xrecorder.SepRecorderOption(cfg.File.Sep),
)
}
if cfg.TCP != nil && cfg.TCP.Addr != "" {
return xrecorder.TCPRecorder(cfg.TCP.Addr, xrecorder.TimeoutTCPRecorderOption(cfg.TCP.Timeout))
}
if cfg.HTTP != nil && cfg.HTTP.URL != "" {
return xrecorder.HTTPRecorder(cfg.HTTP.URL, xrecorder.TimeoutHTTPRecorderOption(cfg.HTTP.Timeout))
}
if cfg.Redis != nil &&
cfg.Redis.Addr != "" &&
cfg.Redis.Key != "" {
switch cfg.Redis.Type {
case "list": // redis list
return xrecorder.RedisListRecorder(cfg.Redis.Addr,
xrecorder.DBRedisRecorderOption(cfg.Redis.DB),
xrecorder.KeyRedisRecorderOption(cfg.Redis.Key),
xrecorder.PasswordRedisRecorderOption(cfg.Redis.Password),
)
case "sset": // sorted set
return xrecorder.RedisSortedSetRecorder(cfg.Redis.Addr,
xrecorder.DBRedisRecorderOption(cfg.Redis.DB),
xrecorder.KeyRedisRecorderOption(cfg.Redis.Key),
xrecorder.PasswordRedisRecorderOption(cfg.Redis.Password),
)
default: // redis set
return xrecorder.RedisSetRecorder(cfg.Redis.Addr,
xrecorder.DBRedisRecorderOption(cfg.Redis.DB),
xrecorder.KeyRedisRecorderOption(cfg.Redis.Key),
xrecorder.PasswordRedisRecorderOption(cfg.Redis.Password),
)
}
}
return
}

View File

@ -0,0 +1,67 @@
package resolver
import (
"crypto/tls"
"net"
"strings"
"github.com/go-gost/core/logger"
"github.com/go-gost/core/resolver"
"github.com/go-gost/x/config"
"github.com/go-gost/x/internal/plugin"
"github.com/go-gost/x/registry"
xresolver "github.com/go-gost/x/resolver"
)
func ParseResolver(cfg *config.ResolverConfig) (resolver.Resolver, error) {
if cfg == nil {
return nil, nil
}
if cfg.Plugin != nil {
var tlsCfg *tls.Config
if cfg.Plugin.TLS != nil {
tlsCfg = &tls.Config{
ServerName: cfg.Plugin.TLS.ServerName,
InsecureSkipVerify: !cfg.Plugin.TLS.Secure,
}
}
switch strings.ToLower(cfg.Plugin.Type) {
case "http":
return xresolver.NewHTTPPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TLSConfigOption(tlsCfg),
plugin.TimeoutOption(cfg.Plugin.Timeout),
), nil
default:
return xresolver.NewGRPCPlugin(
cfg.Name, cfg.Plugin.Addr,
plugin.TokenOption(cfg.Plugin.Token),
plugin.TLSConfigOption(tlsCfg),
)
}
}
var nameservers []xresolver.NameServer
for _, server := range cfg.Nameservers {
nameservers = append(nameservers, xresolver.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 xresolver.NewResolver(
nameservers,
xresolver.LoggerOption(
logger.Default().WithFields(map[string]any{
"kind": "resolver",
"resolver": cfg.Name,
}),
),
)
}

View File

@ -0,0 +1,75 @@
package selector
import (
"github.com/go-gost/core/chain"
"github.com/go-gost/core/selector"
"github.com/go-gost/x/config"
xs "github.com/go-gost/x/selector"
)
func ParseChainSelector(cfg *config.SelectorConfig) selector.Selector[chain.Chainer] {
if cfg == nil {
return nil
}
var strategy selector.Strategy[chain.Chainer]
switch cfg.Strategy {
case "round", "rr":
strategy = xs.RoundRobinStrategy[chain.Chainer]()
case "random", "rand":
strategy = xs.RandomStrategy[chain.Chainer]()
case "fifo", "ha":
strategy = xs.FIFOStrategy[chain.Chainer]()
case "hash":
strategy = xs.HashStrategy[chain.Chainer]()
default:
strategy = xs.RoundRobinStrategy[chain.Chainer]()
}
return xs.NewSelector(
strategy,
xs.FailFilter[chain.Chainer](cfg.MaxFails, cfg.FailTimeout),
xs.BackupFilter[chain.Chainer](),
)
}
func ParseNodeSelector(cfg *config.SelectorConfig) selector.Selector[*chain.Node] {
if cfg == nil {
return nil
}
var strategy selector.Strategy[*chain.Node]
switch cfg.Strategy {
case "round", "rr":
strategy = xs.RoundRobinStrategy[*chain.Node]()
case "random", "rand":
strategy = xs.RandomStrategy[*chain.Node]()
case "fifo", "ha":
strategy = xs.FIFOStrategy[*chain.Node]()
case "hash":
strategy = xs.HashStrategy[*chain.Node]()
default:
strategy = xs.RoundRobinStrategy[*chain.Node]()
}
return xs.NewSelector(
strategy,
xs.FailFilter[*chain.Node](cfg.MaxFails, cfg.FailTimeout),
xs.BackupFilter[*chain.Node](),
)
}
func DefaultNodeSelector() selector.Selector[*chain.Node] {
return xs.NewSelector(
xs.RoundRobinStrategy[*chain.Node](),
xs.FailFilter[*chain.Node](xs.DefaultMaxFails, xs.DefaultFailTimeout),
xs.BackupFilter[*chain.Node](),
)
}
func DefaultChainSelector() selector.Selector[chain.Chainer] {
return xs.NewSelector(
xs.RoundRobinStrategy[chain.Chainer](),
xs.FailFilter[chain.Chainer](xs.DefaultMaxFails, xs.DefaultFailTimeout),
xs.BackupFilter[chain.Chainer](),
)
}

View File

@ -1,4 +1,5 @@
package parsing package service
import ( import (
"fmt" "fmt"
@ -8,6 +9,7 @@ import (
"github.com/go-gost/core/auth" "github.com/go-gost/core/auth"
"github.com/go-gost/core/bypass" "github.com/go-gost/core/bypass"
"github.com/go-gost/core/chain" "github.com/go-gost/core/chain"
"github.com/go-gost/core/hop"
"github.com/go-gost/core/handler" "github.com/go-gost/core/handler"
"github.com/go-gost/core/listener" "github.com/go-gost/core/listener"
"github.com/go-gost/core/logger" "github.com/go-gost/core/logger"
@ -17,6 +19,12 @@ import (
"github.com/go-gost/core/service" "github.com/go-gost/core/service"
xchain "github.com/go-gost/x/chain" xchain "github.com/go-gost/x/chain"
"github.com/go-gost/x/config" "github.com/go-gost/x/config"
"github.com/go-gost/x/config/parsing"
auth_parser "github.com/go-gost/x/config/parsing/auth"
hop_parser "github.com/go-gost/x/config/parsing/hop"
bypass_parser "github.com/go-gost/x/config/parsing/bypass"
selector_parser "github.com/go-gost/x/config/parsing/selector"
admission_parser "github.com/go-gost/x/config/parsing/admission"
tls_util "github.com/go-gost/x/internal/util/tls" tls_util "github.com/go-gost/x/internal/util/tls"
"github.com/go-gost/x/metadata" "github.com/go-gost/x/metadata"
"github.com/go-gost/x/registry" "github.com/go-gost/x/registry"
@ -56,12 +64,12 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
return nil, err return nil, err
} }
if tlsConfig == nil { if tlsConfig == nil {
tlsConfig = defaultTLSConfig.Clone() tlsConfig = parsing.DefaultTLSConfig().Clone()
} }
authers := autherList(cfg.Listener.Auther, cfg.Listener.Authers...) authers := auth_parser.List(cfg.Listener.Auther, cfg.Listener.Authers...)
if len(authers) == 0 { if len(authers) == 0 {
if auther := ParseAutherFromAuth(cfg.Listener.Auth); auther != nil { if auther := auth_parser.ParseAutherFromAuth(cfg.Listener.Auth); auther != nil {
authers = append(authers, auther) authers = append(authers, auther)
} }
} }
@ -70,7 +78,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
auther = auth.AuthenticatorGroup(authers...) auther = auth.AuthenticatorGroup(authers...)
} }
admissions := admissionList(cfg.Admission, cfg.Admissions...) admissions := admission_parser.List(cfg.Admission, cfg.Admissions...)
var sockOpts *chain.SockOpts var sockOpts *chain.SockOpts
if cfg.SockOpts != nil { if cfg.SockOpts != nil {
@ -85,26 +93,26 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
var ignoreChain bool var ignoreChain bool
if cfg.Metadata != nil { if cfg.Metadata != nil {
md := metadata.NewMetadata(cfg.Metadata) md := metadata.NewMetadata(cfg.Metadata)
ppv = mdutil.GetInt(md, mdKeyProxyProtocol) ppv = mdutil.GetInt(md, parsing.MDKeyProxyProtocol)
if v := mdutil.GetString(md, mdKeyInterface); v != "" { if v := mdutil.GetString(md, parsing.MDKeyInterface); v != "" {
ifce = v ifce = v
} }
if v := mdutil.GetInt(md, mdKeySoMark); v > 0 { if v := mdutil.GetInt(md, parsing.MDKeySoMark); v > 0 {
sockOpts = &chain.SockOpts{ sockOpts = &chain.SockOpts{
Mark: v, Mark: v,
} }
} }
preUp = mdutil.GetStrings(md, mdKeyPreUp) preUp = mdutil.GetStrings(md, parsing.MDKeyPreUp)
preDown = mdutil.GetStrings(md, mdKeyPreDown) preDown = mdutil.GetStrings(md, parsing.MDKeyPreDown)
postUp = mdutil.GetStrings(md, mdKeyPostUp) postUp = mdutil.GetStrings(md, parsing.MDKeyPostUp)
postDown = mdutil.GetStrings(md, mdKeyPostDown) postDown = mdutil.GetStrings(md, parsing.MDKeyPostDown)
ignoreChain = mdutil.GetBool(md, mdKeyIgnoreChain) ignoreChain = mdutil.GetBool(md, parsing.MDKeyIgnoreChain)
} }
listenOpts := []listener.Option{ listenOpts := []listener.Option{
listener.AddrOption(cfg.Addr), listener.AddrOption(cfg.Addr),
listener.AutherOption(auther), listener.AutherOption(auther),
listener.AuthOption(parseAuth(cfg.Listener.Auth)), listener.AuthOption(auth_parser.Info(cfg.Listener.Auth)),
listener.TLSConfigOption(tlsConfig), listener.TLSConfigOption(tlsConfig),
listener.AdmissionOption(admission.AdmissionGroup(admissions...)), listener.AdmissionOption(admission.AdmissionGroup(admissions...)),
listener.TrafficLimiterOption(registry.TrafficLimiterRegistry().Get(cfg.Limiter)), listener.TrafficLimiterOption(registry.TrafficLimiterRegistry().Get(cfg.Limiter)),
@ -150,12 +158,12 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
return nil, err return nil, err
} }
if tlsConfig == nil { if tlsConfig == nil {
tlsConfig = defaultTLSConfig.Clone() tlsConfig = parsing.DefaultTLSConfig().Clone()
} }
authers = autherList(cfg.Handler.Auther, cfg.Handler.Authers...) authers = auth_parser.List(cfg.Handler.Auther, cfg.Handler.Authers...)
if len(authers) == 0 { if len(authers) == 0 {
if auther := ParseAutherFromAuth(cfg.Handler.Auth); auther != nil { if auther := auth_parser.ParseAutherFromAuth(cfg.Handler.Auth); auther != nil {
authers = append(authers, auther) authers = append(authers, auther)
} }
} }
@ -172,9 +180,9 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
Recorder: registry.RecorderRegistry().Get(r.Name), Recorder: registry.RecorderRegistry().Get(r.Name),
Record: r.Record, Record: r.Record,
Options: &recorder.Options{ Options: &recorder.Options{
Direction: mdutil.GetBool(md, mdKeyRecorderDirection), Direction: mdutil.GetBool(md, parsing.MDKeyRecorderDirection),
TimestampFormat: mdutil.GetString(md, mdKeyRecorderTimestampFormat), TimestampFormat: mdutil.GetString(md, parsing.MDKeyRecorderTimestampFormat),
Hexdump: mdutil.GetBool(md, mdKeyRecorderHexdump), Hexdump: mdutil.GetBool(md, parsing.MDKeyRecorderHexdump),
}, },
}) })
} }
@ -201,8 +209,8 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
h = rf( h = rf(
handler.RouterOption(router), handler.RouterOption(router),
handler.AutherOption(auther), handler.AutherOption(auther),
handler.AuthOption(parseAuth(cfg.Handler.Auth)), handler.AuthOption(auth_parser.Info(cfg.Handler.Auth)),
handler.BypassOption(bypass.BypassGroup(bypassList(cfg.Bypass, cfg.Bypasses...)...)), handler.BypassOption(bypass.BypassGroup(bypass_parser.List(cfg.Bypass, cfg.Bypasses...)...)),
handler.TLSConfigOption(tlsConfig), handler.TLSConfigOption(tlsConfig),
handler.RateLimiterOption(registry.RateLimiterRegistry().Get(cfg.RLimiter)), handler.RateLimiterOption(registry.RateLimiterRegistry().Get(cfg.RLimiter)),
handler.LoggerOption(handlerLogger), handler.LoggerOption(handlerLogger),
@ -243,7 +251,7 @@ func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
return s, nil return s, nil
} }
func parseForwarder(cfg *config.ForwarderConfig) (chain.Hop, error) { func parseForwarder(cfg *config.ForwarderConfig) (hop.Hop, error) {
if cfg == nil { if cfg == nil {
return nil, nil return nil, nil
} }
@ -284,51 +292,11 @@ func parseForwarder(cfg *config.ForwarderConfig) (chain.Hop, error) {
} }
if len(hc.Nodes) > 0 { if len(hc.Nodes) > 0 {
return ParseHop(&hc) return hop_parser.ParseHop(&hc)
} }
return registry.HopRegistry().Get(hc.Name), nil return registry.HopRegistry().Get(hc.Name), nil
} }
func bypassList(name string, names ...string) []bypass.Bypass {
var bypasses []bypass.Bypass
if bp := registry.BypassRegistry().Get(name); bp != nil {
bypasses = append(bypasses, bp)
}
for _, s := range names {
if bp := registry.BypassRegistry().Get(s); bp != nil {
bypasses = append(bypasses, bp)
}
}
return bypasses
}
func autherList(name string, names ...string) []auth.Authenticator {
var authers []auth.Authenticator
if auther := registry.AutherRegistry().Get(name); auther != nil {
authers = append(authers, auther)
}
for _, s := range names {
if auther := registry.AutherRegistry().Get(s); auther != nil {
authers = append(authers, auther)
}
}
return authers
}
func admissionList(name string, names ...string) []admission.Admission {
var admissions []admission.Admission
if adm := registry.AdmissionRegistry().Get(name); adm != nil {
admissions = append(admissions, adm)
}
for _, s := range names {
if adm := registry.AdmissionRegistry().Get(s); adm != nil {
admissions = append(admissions, adm)
}
}
return admissions
}
func chainGroup(name string, group *config.ChainGroupConfig) chain.Chainer { func chainGroup(name string, group *config.ChainGroupConfig) chain.Chainer {
var chains []chain.Chainer var chains []chain.Chainer
var sel selector.Selector[chain.Chainer] var sel selector.Selector[chain.Chainer]
@ -342,14 +310,14 @@ func chainGroup(name string, group *config.ChainGroupConfig) chain.Chainer {
chains = append(chains, c) chains = append(chains, c)
} }
} }
sel = parseChainSelector(group.Selector) sel = selector_parser.ParseChainSelector(group.Selector)
} }
if len(chains) == 0 { if len(chains) == 0 {
return nil return nil
} }
if sel == nil { if sel == nil {
sel = defaultChainSelector() sel = selector_parser.DefaultChainSelector()
} }
return xchain.NewChainGroup(chains...). return xchain.NewChainGroup(chains...).

View File

@ -23,6 +23,10 @@ var (
defaultTLSConfig *tls.Config defaultTLSConfig *tls.Config
) )
func DefaultTLSConfig() *tls.Config {
return defaultTLSConfig
}
func BuildDefaultTLSConfig(cfg *config.TLSConfig) { func BuildDefaultTLSConfig(cfg *config.TLSConfig) {
log := logger.Default() log := logger.Default()

4
go.mod
View File

@ -7,10 +7,10 @@ require (
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
github.com/gin-contrib/cors v1.3.1 github.com/gin-contrib/cors v1.3.1
github.com/gin-gonic/gin v1.9.1 github.com/gin-gonic/gin v1.9.1
github.com/go-gost/core v0.0.0-20230920145336-6d0e88635be9 github.com/go-gost/core v0.0.0-20230928130125-b0bd45c1b862
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-20230921115816-47001719099f github.com/go-gost/plugin v0.0.0-20230928130211-8bc0679b5c15
github.com/go-gost/relay v0.4.1-0.20230916134211-828f314ddfe7 github.com/go-gost/relay v0.4.1-0.20230916134211-828f314ddfe7
github.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451 github.com/go-gost/tls-dissector v0.0.2-0.20220408131628-aac992c27451
github.com/go-redis/redis/v8 v8.11.5 github.com/go-redis/redis/v8 v8.11.5

8
go.sum
View File

@ -100,14 +100,14 @@ github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SU
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 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-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-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gost/core v0.0.0-20230920145336-6d0e88635be9 h1:VHka8LcdBJmM7Yv2bjQO5kctF0T9O4E/PVzgkdk0Vdo= github.com/go-gost/core v0.0.0-20230928130125-b0bd45c1b862 h1:hbCHyfYE96WZefTBitiL35FCYxHCgEWpS+W/5oCyEXk=
github.com/go-gost/core v0.0.0-20230920145336-6d0e88635be9/go.mod h1:ndkgWVYRLwupVaFFWv8ML1Nr8tD3xhHK245PLpUDg4E= github.com/go-gost/core v0.0.0-20230928130125-b0bd45c1b862/go.mod h1:ndkgWVYRLwupVaFFWv8ML1Nr8tD3xhHK245PLpUDg4E=
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=
github.com/go-gost/gosocks5 v0.4.0/go.mod h1:1G6I7HP7VFVxveGkoK8mnprnJqSqJjdcASKsdUn4Pp4= github.com/go-gost/gosocks5 v0.4.0/go.mod h1:1G6I7HP7VFVxveGkoK8mnprnJqSqJjdcASKsdUn4Pp4=
github.com/go-gost/plugin v0.0.0-20230921115816-47001719099f h1:Z6k8xfvQv8PmrC++wV4BlzVv85iuGtHhL6QSzrF6m5Q= github.com/go-gost/plugin v0.0.0-20230928130211-8bc0679b5c15 h1:SKPbGuJUBKhh4qE2G5juT4PNMrzYH86itiY3TGwvYcs=
github.com/go-gost/plugin v0.0.0-20230921115816-47001719099f/go.mod h1:mM/RLNsVy2nz5PiOijuqLYR3LhMzyQ9Kh/p0rXybJoo= github.com/go-gost/plugin v0.0.0-20230928130211-8bc0679b5c15/go.mod h1:mM/RLNsVy2nz5PiOijuqLYR3LhMzyQ9Kh/p0rXybJoo=
github.com/go-gost/relay v0.4.1-0.20230916134211-828f314ddfe7 h1:qAG1OyjvdA5h221CfFSS3J359V3d2E7dJWyP29QoDSI= github.com/go-gost/relay v0.4.1-0.20230916134211-828f314ddfe7 h1:qAG1OyjvdA5h221CfFSS3J359V3d2E7dJWyP29QoDSI=
github.com/go-gost/relay v0.4.1-0.20230916134211-828f314ddfe7/go.mod h1:lcX+23LCQ3khIeASBo+tJ/WbwXFO32/N5YN6ucuYTG8= github.com/go-gost/relay v0.4.1-0.20230916134211-828f314ddfe7/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=

View File

@ -11,10 +11,11 @@ import (
"github.com/go-gost/core/chain" "github.com/go-gost/core/chain"
"github.com/go-gost/core/common/bufpool" "github.com/go-gost/core/common/bufpool"
"github.com/go-gost/core/handler" "github.com/go-gost/core/handler"
"github.com/go-gost/core/hop"
"github.com/go-gost/core/hosts" "github.com/go-gost/core/hosts"
"github.com/go-gost/core/logger" "github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata" md "github.com/go-gost/core/metadata"
xchain "github.com/go-gost/x/chain" xhop "github.com/go-gost/x/hop"
resolver_util "github.com/go-gost/x/internal/util/resolver" resolver_util "github.com/go-gost/x/internal/util/resolver"
"github.com/go-gost/x/registry" "github.com/go-gost/x/registry"
"github.com/go-gost/x/resolver/exchanger" "github.com/go-gost/x/resolver/exchanger"
@ -30,7 +31,7 @@ func init() {
} }
type dnsHandler struct { type dnsHandler struct {
hop chain.Hop hop hop.Hop
exchangers map[string]exchanger.Exchanger exchangers map[string]exchanger.Exchanger
cache *resolver_util.Cache cache *resolver_util.Cache
router *chain.Router router *chain.Router
@ -70,10 +71,14 @@ func (h *dnsHandler) Init(md md.Metadata) (err error) {
for i, addr := range h.md.dns { for i, addr := range h.md.dns {
nodes = append(nodes, chain.NewNode(fmt.Sprintf("target-%d", i), addr)) nodes = append(nodes, chain.NewNode(fmt.Sprintf("target-%d", i), addr))
} }
h.hop = xchain.NewChainHop(nodes) h.hop = xhop.NewHop(xhop.NodeOption(nodes...))
} }
for _, node := range h.hop.Nodes() { var nodes []*chain.Node
if nl, ok := h.hop.(hop.NodeList); ok {
nodes = nl.Nodes()
}
for _, node := range nodes {
addr := strings.TrimSpace(node.Addr) addr := strings.TrimSpace(node.Addr)
if addr == "" { if addr == "" {
continue continue
@ -109,7 +114,7 @@ func (h *dnsHandler) Init(md md.Metadata) (err error) {
} }
// Forward implements handler.Forwarder. // Forward implements handler.Forwarder.
func (h *dnsHandler) Forward(hop chain.Hop) { func (h *dnsHandler) Forward(hop hop.Hop) {
h.hop = hop h.hop = hop
} }
@ -325,7 +330,7 @@ func (h *dnsHandler) selectExchanger(ctx context.Context, addr string) exchanger
if h.hop == nil { if h.hop == nil {
return nil return nil
} }
node := h.hop.Select(ctx, chain.AddrSelectOption(addr)) node := h.hop.Select(ctx, hop.AddrSelectOption(addr))
if node == nil { if node == nil {
return nil return nil
} }

View File

@ -15,6 +15,7 @@ import (
"github.com/go-gost/core/chain" "github.com/go-gost/core/chain"
"github.com/go-gost/core/handler" "github.com/go-gost/core/handler"
"github.com/go-gost/core/hop"
"github.com/go-gost/core/logger" "github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata" md "github.com/go-gost/core/metadata"
xnet "github.com/go-gost/x/internal/net" xnet "github.com/go-gost/x/internal/net"
@ -30,7 +31,7 @@ func init() {
} }
type forwardHandler struct { type forwardHandler struct {
hop chain.Hop hop hop.Hop
router *chain.Router router *chain.Router
md metadata md metadata
options handler.Options options handler.Options
@ -61,7 +62,7 @@ func (h *forwardHandler) Init(md md.Metadata) (err error) {
} }
// Forward implements handler.Forwarder. // Forward implements handler.Forwarder.
func (h *forwardHandler) Forward(hop chain.Hop) { func (h *forwardHandler) Forward(hop hop.Hop) {
h.hop = hop h.hop = hop
} }
@ -123,8 +124,8 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
} }
if h.hop != nil { if h.hop != nil {
target = h.hop.Select(ctx, target = h.hop.Select(ctx,
chain.HostSelectOption(host), hop.HostSelectOption(host),
chain.ProtocolSelectOption(protocol), hop.ProtocolSelectOption(protocol),
) )
} }
if target == nil { if target == nil {
@ -192,8 +193,8 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, log l
} }
if h.hop != nil { if h.hop != nil {
target = h.hop.Select(ctx, target = h.hop.Select(ctx,
chain.HostSelectOption(req.Host), hop.HostSelectOption(req.Host),
chain.ProtocolSelectOption(forward.ProtoHTTP), hop.ProtocolSelectOption(forward.ProtoHTTP),
) )
} }
if target == nil { if target == nil {

View File

@ -15,6 +15,7 @@ import (
"github.com/go-gost/core/chain" "github.com/go-gost/core/chain"
"github.com/go-gost/core/handler" "github.com/go-gost/core/handler"
"github.com/go-gost/core/hop"
"github.com/go-gost/core/logger" "github.com/go-gost/core/logger"
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"
@ -30,7 +31,7 @@ func init() {
} }
type forwardHandler struct { type forwardHandler struct {
hop chain.Hop hop hop.Hop
router *chain.Router router *chain.Router
md metadata md metadata
options handler.Options options handler.Options
@ -61,7 +62,7 @@ func (h *forwardHandler) Init(md mdata.Metadata) (err error) {
} }
// Forward implements handler.Forwarder. // Forward implements handler.Forwarder.
func (h *forwardHandler) Forward(hop chain.Hop) { func (h *forwardHandler) Forward(hop hop.Hop) {
h.hop = hop h.hop = hop
} }
@ -123,8 +124,8 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand
} }
if h.hop != nil { if h.hop != nil {
target = h.hop.Select(ctx, target = h.hop.Select(ctx,
chain.HostSelectOption(host), hop.HostSelectOption(host),
chain.ProtocolSelectOption(protocol), hop.ProtocolSelectOption(protocol),
) )
} }
if target == nil { if target == nil {
@ -189,8 +190,8 @@ func (h *forwardHandler) handleHTTP(ctx context.Context, rw io.ReadWriter, log l
} }
if h.hop != nil { if h.hop != nil {
target = h.hop.Select(ctx, target = h.hop.Select(ctx,
chain.HostSelectOption(req.Host), hop.HostSelectOption(req.Host),
chain.ProtocolSelectOption(forward.ProtoHTTP), hop.ProtocolSelectOption(forward.ProtoHTTP),
) )
} }
if target == nil { if target == nil {

View File

@ -10,6 +10,7 @@ import (
"time" "time"
"github.com/go-gost/core/chain" "github.com/go-gost/core/chain"
"github.com/go-gost/core/hop"
"github.com/go-gost/core/handler" "github.com/go-gost/core/handler"
"github.com/go-gost/core/logger" "github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata" md "github.com/go-gost/core/metadata"
@ -22,7 +23,7 @@ func init() {
} }
type http3Handler struct { type http3Handler struct {
hop chain.Hop hop hop.Hop
router *chain.Router router *chain.Router
md metadata md metadata
options handler.Options options handler.Options
@ -53,7 +54,7 @@ func (h *http3Handler) Init(md md.Metadata) error {
} }
// Forward implements handler.Forwarder. // Forward implements handler.Forwarder.
func (h *http3Handler) Forward(hop chain.Hop) { func (h *http3Handler) Forward(hop hop.Hop) {
h.hop = hop h.hop = hop
} }
@ -118,7 +119,7 @@ func (h *http3Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
var target *chain.Node var target *chain.Node
if h.hop != nil { if h.hop != nil {
target = h.hop.Select(ctx, chain.HostSelectOption(addr)) target = h.hop.Select(ctx, hop.HostSelectOption(addr))
} }
if target == nil { if target == nil {
err := errors.New("target not available") err := errors.New("target not available")

View File

@ -10,6 +10,7 @@ import (
"github.com/go-gost/core/chain" "github.com/go-gost/core/chain"
"github.com/go-gost/core/handler" "github.com/go-gost/core/handler"
"github.com/go-gost/core/hop"
"github.com/go-gost/core/listener" "github.com/go-gost/core/listener"
md "github.com/go-gost/core/metadata" md "github.com/go-gost/core/metadata"
"github.com/go-gost/core/service" "github.com/go-gost/core/service"
@ -32,7 +33,7 @@ func init() {
} }
type relayHandler struct { type relayHandler struct {
hop chain.Hop hop hop.Hop
router *chain.Router router *chain.Router
md metadata md metadata
options handler.Options options handler.Options
@ -124,7 +125,7 @@ func (h *relayHandler) initEntryPoint() (err error) {
} }
// Forward implements handler.Forwarder. // Forward implements handler.Forwarder.
func (h *relayHandler) Forward(hop chain.Hop) { func (h *relayHandler) Forward(hop hop.Hop) {
h.hop = hop h.hop = hop
} }

View File

@ -9,6 +9,7 @@ import (
"github.com/go-gost/core/chain" "github.com/go-gost/core/chain"
"github.com/go-gost/core/handler" "github.com/go-gost/core/handler"
"github.com/go-gost/core/hop"
"github.com/go-gost/core/logger" "github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata" md "github.com/go-gost/core/metadata"
"github.com/go-gost/core/recorder" "github.com/go-gost/core/recorder"
@ -24,7 +25,7 @@ func init() {
} }
type serialHandler struct { type serialHandler struct {
hop chain.Hop hop hop.Hop
router *chain.Router router *chain.Router
md metadata md metadata
options handler.Options options handler.Options
@ -64,7 +65,7 @@ func (h *serialHandler) Init(md md.Metadata) (err error) {
} }
// Forward implements handler.Forwarder. // Forward implements handler.Forwarder.
func (h *serialHandler) Forward(hop chain.Hop) { func (h *serialHandler) Forward(hop hop.Hop) {
h.hop = hop h.hop = hop
} }

View File

@ -13,6 +13,7 @@ import (
"github.com/go-gost/core/chain" "github.com/go-gost/core/chain"
"github.com/go-gost/core/common/bufpool" "github.com/go-gost/core/common/bufpool"
"github.com/go-gost/core/handler" "github.com/go-gost/core/handler"
"github.com/go-gost/core/hop"
"github.com/go-gost/core/logger" "github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata" md "github.com/go-gost/core/metadata"
"github.com/go-gost/x/internal/util/ss" "github.com/go-gost/x/internal/util/ss"
@ -28,7 +29,7 @@ func init() {
} }
type tapHandler struct { type tapHandler struct {
hop chain.Hop hop hop.Hop
routes sync.Map routes sync.Map
exit chan struct{} exit chan struct{}
cipher core.Cipher cipher core.Cipher
@ -72,7 +73,7 @@ func (h *tapHandler) Init(md md.Metadata) (err error) {
} }
// Forward implements handler.Forwarder. // Forward implements handler.Forwarder.
func (h *tapHandler) Forward(hop chain.Hop) { func (h *tapHandler) Forward(hop hop.Hop) {
h.hop = hop h.hop = hop
} }

View File

@ -9,6 +9,7 @@ import (
"time" "time"
"github.com/go-gost/core/chain" "github.com/go-gost/core/chain"
"github.com/go-gost/core/hop"
"github.com/go-gost/core/handler" "github.com/go-gost/core/handler"
md "github.com/go-gost/core/metadata" md "github.com/go-gost/core/metadata"
tun_util "github.com/go-gost/x/internal/util/tun" tun_util "github.com/go-gost/x/internal/util/tun"
@ -26,7 +27,7 @@ func init() {
} }
type tunHandler struct { type tunHandler struct {
hop chain.Hop hop hop.Hop
routes sync.Map routes sync.Map
router *chain.Router router *chain.Router
md metadata md metadata
@ -58,7 +59,7 @@ func (h *tunHandler) Init(md md.Metadata) (err error) {
} }
// Forward implements handler.Forwarder. // Forward implements handler.Forwarder.
func (h *tunHandler) Forward(hop chain.Hop) { func (h *tunHandler) Forward(hop hop.Hop) {
h.hop = hop h.hop = hop
} }

View File

@ -8,6 +8,7 @@ import (
"time" "time"
"github.com/go-gost/core/chain" "github.com/go-gost/core/chain"
"github.com/go-gost/core/hop"
"github.com/go-gost/core/handler" "github.com/go-gost/core/handler"
"github.com/go-gost/core/logger" "github.com/go-gost/core/logger"
md "github.com/go-gost/core/metadata" md "github.com/go-gost/core/metadata"
@ -20,7 +21,7 @@ func init() {
} }
type unixHandler struct { type unixHandler struct {
hop chain.Hop hop hop.Hop
router *chain.Router router *chain.Router
md metadata md metadata
options handler.Options options handler.Options
@ -51,7 +52,7 @@ func (h *unixHandler) Init(md md.Metadata) (err error) {
} }
// Forward implements handler.Forwarder. // Forward implements handler.Forwarder.
func (h *unixHandler) Forward(hop chain.Hop) { func (h *unixHandler) Forward(hop hop.Hop) {
h.hop = hop h.hop = hop
} }

307
hop/hop.go Normal file
View File

@ -0,0 +1,307 @@
package hop
import (
"bytes"
"context"
"encoding/json"
"io"
"net"
"strings"
"sync"
"time"
"github.com/go-gost/core/bypass"
"github.com/go-gost/core/chain"
"github.com/go-gost/core/hop"
"github.com/go-gost/core/logger"
"github.com/go-gost/core/selector"
"github.com/go-gost/x/config"
node_parser "github.com/go-gost/x/config/parsing/node"
"github.com/go-gost/x/internal/loader"
)
type options struct {
name string
nodes []*chain.Node
bypass bypass.Bypass
selector selector.Selector[*chain.Node]
fileLoader loader.Loader
redisLoader loader.Loader
httpLoader loader.Loader
period time.Duration
logger logger.Logger
}
type Option func(*options)
func NameOption(name string) Option {
return func(o *options) {
o.name = name
}
}
func NodeOption(nodes ...*chain.Node) Option {
return func(o *options) {
o.nodes = nodes
}
}
func BypassOption(bp bypass.Bypass) Option {
return func(o *options) {
o.bypass = bp
}
}
func SelectorOption(s selector.Selector[*chain.Node]) Option {
return func(o *options) {
o.selector = s
}
}
func ReloadPeriodOption(period time.Duration) Option {
return func(opts *options) {
opts.period = period
}
}
func FileLoaderOption(fileLoader loader.Loader) Option {
return func(opts *options) {
opts.fileLoader = fileLoader
}
}
func RedisLoaderOption(redisLoader loader.Loader) Option {
return func(opts *options) {
opts.redisLoader = redisLoader
}
}
func HTTPLoaderOption(httpLoader loader.Loader) Option {
return func(opts *options) {
opts.httpLoader = httpLoader
}
}
func LoggerOption(logger logger.Logger) Option {
return func(opts *options) {
opts.logger = logger
}
}
type chainHop struct {
nodes []*chain.Node
mu sync.RWMutex
cancelFunc context.CancelFunc
options options
}
func NewHop(opts ...Option) hop.Hop {
var options options
for _, opt := range opts {
if opt != nil {
opt(&options)
}
}
ctx, cancel := context.WithCancel(context.TODO())
p := &chainHop{
cancelFunc: cancel,
options: options,
}
if err := p.reload(ctx); err != nil {
options.logger.Warnf("reload: %v", err)
}
if p.options.period > 0 {
go p.periodReload(ctx)
}
return p
}
func (p *chainHop) Nodes() []*chain.Node {
if p == nil {
return nil
}
p.mu.RLock()
defer p.mu.RUnlock()
return p.nodes
}
func (p *chainHop) Select(ctx context.Context, opts ...hop.SelectOption) *chain.Node {
var options hop.SelectOptions
for _, opt := range opts {
opt(&options)
}
ns := p.Nodes()
if len(ns) == 0 {
return nil
}
// hop level bypass
if p.options.bypass != nil &&
p.options.bypass.Contains(ctx, options.Addr) {
return nil
}
filters := ns
if host := options.Host; host != "" {
filters = nil
if v, _, _ := net.SplitHostPort(host); v != "" {
host = v
}
var nodes []*chain.Node
for _, node := range ns {
if node == nil {
continue
}
vhost := node.Options().Host
if vhost == "" {
nodes = append(nodes, node)
continue
}
if vhost == host ||
vhost[0] == '.' && strings.HasSuffix(host, vhost[1:]) {
filters = append(filters, node)
}
}
if len(filters) == 0 {
filters = nodes
}
} else if protocol := options.Protocol; protocol != "" {
filters = nil
for _, node := range ns {
if node == nil {
continue
}
if node.Options().Protocol == protocol {
filters = append(filters, node)
}
}
}
var nodes []*chain.Node
for _, node := range filters {
if node == nil {
continue
}
// node level bypass
if node.Options().Bypass != nil &&
node.Options().Bypass.Contains(ctx, options.Addr) {
continue
}
nodes = append(nodes, node)
}
if len(nodes) == 0 {
return nil
}
if s := p.options.selector; s != nil {
return s.Select(ctx, nodes...)
}
return nodes[0]
}
func (p *chainHop) periodReload(ctx context.Context) error {
period := p.options.period
if period < time.Second {
period = time.Second
}
ticker := time.NewTicker(period)
defer ticker.Stop()
for {
select {
case <-ticker.C:
if err := p.reload(ctx); err != nil {
p.options.logger.Warnf("reload: %v", err)
// return err
}
p.options.logger.Debug("hop reload done")
case <-ctx.Done():
return ctx.Err()
}
}
}
func (p *chainHop) reload(ctx context.Context) (err error) {
nodes := p.options.nodes
nl, err := p.load(ctx)
nodes = append(nodes, nl...)
p.mu.Lock()
defer p.mu.Unlock()
p.nodes = nodes
return
}
func (p *chainHop) load(ctx context.Context) (nodes []*chain.Node, err error) {
if p.options.fileLoader != nil {
r, er := p.options.fileLoader.Load(ctx)
if er != nil {
p.options.logger.Warnf("file loader: %v", er)
}
nodes, _ = p.parseNode(r)
}
if p.options.redisLoader != nil {
if lister, ok := p.options.redisLoader.(loader.Lister); ok {
list, er := lister.List(ctx)
if er != nil {
p.options.logger.Warnf("redis loader: %v", er)
}
for _, s := range list {
nl, _ := p.parseNode(bytes.NewReader([]byte(s)))
nodes = append(nodes, nl...)
}
}
}
if p.options.httpLoader != nil {
r, er := p.options.httpLoader.Load(ctx)
if er != nil {
p.options.logger.Warnf("http loader: %v", er)
}
if node, _ := p.parseNode(r); node != nil {
nodes = append(nodes, node...)
}
}
p.options.logger.Debugf("load items %d", len(nodes))
return
}
func (p *chainHop) parseNode(r io.Reader) ([]*chain.Node, error) {
var ncs []*config.NodeConfig
if err := json.NewDecoder(r).Decode(&ncs); err != nil {
return nil, err
}
var nodes []*chain.Node
for _, nc := range ncs {
if nc == nil {
continue
}
node, err := node_parser.ParseNode(p.options.name, nc)
if err != nil {
return nodes, err
}
nodes = append(nodes, node)
}
return nodes, nil
}
func (p *chainHop) Close() error {
p.cancelFunc()
if p.options.fileLoader != nil {
p.options.fileLoader.Close()
}
if p.options.redisLoader != nil {
p.options.redisLoader.Close()
}
return nil
}

204
hop/plugin.go Normal file
View File

@ -0,0 +1,204 @@
package hop
import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"github.com/go-gost/core/chain"
"github.com/go-gost/core/hop"
"github.com/go-gost/core/logger"
"github.com/go-gost/plugin/hop/proto"
"github.com/go-gost/x/config"
node_parser "github.com/go-gost/x/config/parsing/node"
"github.com/go-gost/x/internal/plugin"
auth_util "github.com/go-gost/x/internal/util/auth"
"google.golang.org/grpc"
)
type grpcPlugin struct {
name string
conn grpc.ClientConnInterface
client proto.HopClient
log logger.Logger
}
// NewGRPCPlugin creates a Hop plugin based on gRPC.
func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) hop.Hop{
var options plugin.Options
for _, opt := range opts {
opt(&options)
}
log := logger.Default().WithFields(map[string]any{
"kind": "hop",
"hop": name,
})
conn, err := plugin.NewGRPCConn(addr, &options)
if err != nil {
log.Error(err)
}
p := &grpcPlugin{
name: name,
conn: conn,
log: log,
}
if conn != nil {
p.client = proto.NewHopClient(conn)
}
return p
}
func (p *grpcPlugin) Select(ctx context.Context, opts ...hop.SelectOption) *chain.Node {
if p.client == nil {
return nil
}
var options hop.SelectOptions
for _, opt := range opts {
opt(&options)
}
r, err := p.client.Select(ctx,
&proto.SelectRequest{
Addr: options.Addr,
Host: options.Host,
Client: string(auth_util.IDFromContext(ctx)),
})
if err != nil {
p.log.Error(err)
return nil
}
if r.Node == nil {
return nil
}
var cfg config.NodeConfig
if err := json.NewDecoder(bytes.NewReader(r.Node)).Decode(&cfg); err != nil {
p.log.Error(err)
return nil
}
node, err := node_parser.ParseNode(p.name, &cfg)
if err != nil {
p.log.Error(err)
return nil
}
return node
}
func (p *grpcPlugin) Close() error {
if closer, ok := p.conn.(io.Closer); ok {
return closer.Close()
}
return nil
}
type httpPluginRequest struct {
Network string `json:"network"`
Addr string `json:"addr"`
Host string `json:"host"`
Client string `json:"client"`
}
type httpPluginResponse struct {
Node string `json:"node"`
}
type httpPlugin struct {
name string
url string
client *http.Client
header http.Header
log logger.Logger
}
// NewHTTPPlugin creates an Hop plugin based on HTTP.
func NewHTTPPlugin(name string, url string, opts ...plugin.Option) hop.Hop{
var options plugin.Options
for _, opt := range opts {
opt(&options)
}
return &httpPlugin{
name: name,
url: url,
client: plugin.NewHTTPClient(&options),
header: options.Header,
log: logger.Default().WithFields(map[string]any{
"kind": "hop",
"hop": name,
}),
}
}
func (p *httpPlugin) Select(ctx context.Context, opts ...hop.SelectOption) *chain.Node {
if p.client == nil {
return nil
}
var options hop.SelectOptions
for _, opt := range opts {
opt(&options)
}
rb := httpPluginRequest{
Addr: options.Addr,
Host: options.Host,
Client: string(auth_util.IDFromContext(ctx)),
}
v, err := json.Marshal(&rb)
if err != nil {
p.log.Error(err)
return nil
}
req, err := http.NewRequest(http.MethodPost, p.url, bytes.NewReader(v))
if err != nil {
p.log.Error(err)
return nil
}
if p.header != nil {
req.Header = p.header.Clone()
}
req.Header.Set("Content-Type", "application/json")
resp, err := p.client.Do(req)
if err != nil {
p.log.Error(err)
return nil
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
p.log.Error(resp.Status)
return nil
}
res := httpPluginResponse{}
if err := json.NewDecoder(resp.Body).Decode(&res); err != nil {
p.log.Error(resp.Status)
return nil
}
if res.Node == "" {
return nil
}
var cfg config.NodeConfig
if err := json.NewDecoder(bytes.NewReader([]byte(res.Node))).Decode(&cfg); err != nil {
p.log.Error(err)
return nil
}
node, err := node_parser.ParseNode(p.name, &cfg)
if err != nil {
p.log.Error(err)
return nil
}
return node
}

View File

@ -12,7 +12,6 @@ import (
"github.com/go-gost/core/hosts" "github.com/go-gost/core/hosts"
"github.com/go-gost/core/logger" "github.com/go-gost/core/logger"
"github.com/go-gost/x/internal/loader" "github.com/go-gost/x/internal/loader"
"google.golang.org/grpc"
) )
type Mapping struct { type Mapping struct {
@ -25,7 +24,6 @@ type options struct {
fileLoader loader.Loader fileLoader loader.Loader
redisLoader loader.Loader redisLoader loader.Loader
httpLoader loader.Loader httpLoader loader.Loader
client *grpc.ClientConn
period time.Duration period time.Duration
logger logger.Logger logger logger.Logger
} }
@ -62,12 +60,6 @@ func HTTPLoaderOption(httpLoader loader.Loader) Option {
} }
} }
func PluginConnOption(c *grpc.ClientConn) Option {
return func(opts *options) {
opts.client = c
}
}
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

View File

@ -12,18 +12,18 @@ import (
"github.com/go-gost/core/logger" "github.com/go-gost/core/logger"
"github.com/go-gost/plugin/hosts/proto" "github.com/go-gost/plugin/hosts/proto"
auth_util "github.com/go-gost/x/internal/util/auth" auth_util "github.com/go-gost/x/internal/util/auth"
"github.com/go-gost/x/internal/util/plugin" "github.com/go-gost/x/internal/plugin"
"google.golang.org/grpc" "google.golang.org/grpc"
) )
type grpcPluginHostMapper struct { type grpcPlugin struct {
conn grpc.ClientConnInterface conn grpc.ClientConnInterface
client proto.HostMapperClient client proto.HostMapperClient
log logger.Logger log logger.Logger
} }
// NewGRPCPluginHostMapper creates a HostMapper plugin based on gRPC. // NewGRPCPlugin creates a HostMapper plugin based on gRPC.
func NewGRPCPluginHostMapper(name string, addr string, opts ...plugin.Option) hosts.HostMapper { func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) hosts.HostMapper {
var options plugin.Options var options plugin.Options
for _, opt := range opts { for _, opt := range opts {
opt(&options) opt(&options)
@ -37,7 +37,7 @@ func NewGRPCPluginHostMapper(name string, addr string, opts ...plugin.Option) ho
if err != nil { if err != nil {
log.Error(err) log.Error(err)
} }
p := &grpcPluginHostMapper{ p := &grpcPlugin{
conn: conn, conn: conn,
log: log, log: log,
} }
@ -47,7 +47,7 @@ func NewGRPCPluginHostMapper(name string, addr string, opts ...plugin.Option) ho
return p return p
} }
func (p *grpcPluginHostMapper) Lookup(ctx context.Context, network, host string) (ips []net.IP, ok bool) { func (p *grpcPlugin) Lookup(ctx context.Context, network, host string) (ips []net.IP, ok bool) {
p.log.Debugf("lookup %s/%s", host, network) p.log.Debugf("lookup %s/%s", host, network)
if p.client == nil { if p.client == nil {
@ -73,39 +73,39 @@ func (p *grpcPluginHostMapper) Lookup(ctx context.Context, network, host string)
return return
} }
func (p *grpcPluginHostMapper) Close() error { func (p *grpcPlugin) Close() error {
if closer, ok := p.conn.(io.Closer); ok { if closer, ok := p.conn.(io.Closer); ok {
return closer.Close() return closer.Close()
} }
return nil return nil
} }
type httpHostMapperRequest struct { type httpPluginRequest struct {
Network string `json:"network"` Network string `json:"network"`
Host string `json:"host"` Host string `json:"host"`
Client string `json:"client"` Client string `json:"client"`
} }
type httpHostMapperResponse struct { type httpPluginResponse struct {
IPs []string `json:"ips"` IPs []string `json:"ips"`
OK bool `json:"ok"` OK bool `json:"ok"`
} }
type httpPluginHostMapper struct { type httpPlugin struct {
url string url string
client *http.Client client *http.Client
header http.Header header http.Header
log logger.Logger log logger.Logger
} }
// NewHTTPPluginHostMapper creates an HostMapper plugin based on HTTP. // NewHTTPPlugin creates an HostMapper plugin based on HTTP.
func NewHTTPPluginHostMapper(name string, url string, opts ...plugin.Option) hosts.HostMapper { func NewHTTPPlugin(name string, url string, opts ...plugin.Option) hosts.HostMapper {
var options plugin.Options var options plugin.Options
for _, opt := range opts { for _, opt := range opts {
opt(&options) opt(&options)
} }
return &httpPluginHostMapper{ return &httpPlugin{
url: url, url: url,
client: plugin.NewHTTPClient(&options), client: plugin.NewHTTPClient(&options),
header: options.Header, header: options.Header,
@ -116,14 +116,14 @@ func NewHTTPPluginHostMapper(name string, url string, opts ...plugin.Option) hos
} }
} }
func (p *httpPluginHostMapper) Lookup(ctx context.Context, network, host string) (ips []net.IP, ok bool) { func (p *httpPlugin) Lookup(ctx context.Context, network, host string) (ips []net.IP, ok bool) {
p.log.Debugf("lookup %s/%s", host, network) p.log.Debugf("lookup %s/%s", host, network)
if p.client == nil { if p.client == nil {
return return
} }
rb := httpHostMapperRequest{ rb := httpPluginRequest{
Network: network, Network: network,
Host: host, Host: host,
Client: string(auth_util.IDFromContext(ctx)), Client: string(auth_util.IDFromContext(ctx)),
@ -152,7 +152,7 @@ func (p *httpPluginHostMapper) Lookup(ctx context.Context, network, host string)
return return
} }
res := httpHostMapperResponse{} res := httpPluginResponse{}
if err := json.NewDecoder(resp.Body).Decode(&res); err != nil { if err := json.NewDecoder(resp.Body).Decode(&res); err != nil {
return return
} }

View File

@ -12,7 +12,6 @@ import (
"github.com/go-gost/core/ingress" "github.com/go-gost/core/ingress"
"github.com/go-gost/core/logger" "github.com/go-gost/core/logger"
"github.com/go-gost/x/internal/loader" "github.com/go-gost/x/internal/loader"
"google.golang.org/grpc"
) )
type Rule struct { type Rule struct {
@ -25,7 +24,6 @@ type options struct {
fileLoader loader.Loader fileLoader loader.Loader
redisLoader loader.Loader redisLoader loader.Loader
httpLoader loader.Loader httpLoader loader.Loader
client *grpc.ClientConn
period time.Duration period time.Duration
logger logger.Logger logger logger.Logger
} }
@ -62,12 +60,6 @@ func HTTPLoaderOption(httpLoader loader.Loader) Option {
} }
} }
func PluginConnOption(c *grpc.ClientConn) Option {
return func(opts *options) {
opts.client = c
}
}
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

View File

@ -10,18 +10,18 @@ import (
"github.com/go-gost/core/ingress" "github.com/go-gost/core/ingress"
"github.com/go-gost/core/logger" "github.com/go-gost/core/logger"
"github.com/go-gost/plugin/ingress/proto" "github.com/go-gost/plugin/ingress/proto"
"github.com/go-gost/x/internal/util/plugin" "github.com/go-gost/x/internal/plugin"
"google.golang.org/grpc" "google.golang.org/grpc"
) )
type grpcPluginIngress struct { type grpcPlugin struct {
conn grpc.ClientConnInterface conn grpc.ClientConnInterface
client proto.IngressClient client proto.IngressClient
log logger.Logger log logger.Logger
} }
// NewGRPCPluginIngress creates an Ingress plugin based on gRPC. // NewGRPCPlugin creates an Ingress plugin based on gRPC.
func NewGRPCPluginIngress(name string, addr string, opts ...plugin.Option) ingress.Ingress { func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) ingress.Ingress {
var options plugin.Options var options plugin.Options
for _, opt := range opts { for _, opt := range opts {
opt(&options) opt(&options)
@ -36,7 +36,7 @@ func NewGRPCPluginIngress(name string, addr string, opts ...plugin.Option) ingre
log.Error(err) log.Error(err)
} }
p := &grpcPluginIngress{ p := &grpcPlugin{
conn: conn, conn: conn,
log: log, log: log,
} }
@ -46,7 +46,7 @@ func NewGRPCPluginIngress(name string, addr string, opts ...plugin.Option) ingre
return p return p
} }
func (p *grpcPluginIngress) Get(ctx context.Context, host string) string { func (p *grpcPlugin) Get(ctx context.Context, host string) string {
if p.client == nil { if p.client == nil {
return "" return ""
} }
@ -62,36 +62,36 @@ func (p *grpcPluginIngress) Get(ctx context.Context, host string) string {
return r.GetEndpoint() return r.GetEndpoint()
} }
func (p *grpcPluginIngress) Close() error { func (p *grpcPlugin) Close() error {
if closer, ok := p.conn.(io.Closer); ok { if closer, ok := p.conn.(io.Closer); ok {
return closer.Close() return closer.Close()
} }
return nil return nil
} }
type httpIngressRequest struct { type httpPluginRequest struct {
Host string `json:"host"` Host string `json:"host"`
} }
type httpIngressResponse struct { type httpPluginResponse struct {
Endpoint string `json:"endpoint"` Endpoint string `json:"endpoint"`
} }
type httpPluginIngress struct { type httpPlugin struct {
url string url string
client *http.Client client *http.Client
header http.Header header http.Header
log logger.Logger log logger.Logger
} }
// NewHTTPPluginIngress creates an Ingress plugin based on HTTP. // NewHTTPPlugin creates an Ingress plugin based on HTTP.
func NewHTTPPluginIngress(name string, url string, opts ...plugin.Option) ingress.Ingress { func NewHTTPPlugin(name string, url string, opts ...plugin.Option) ingress.Ingress {
var options plugin.Options var options plugin.Options
for _, opt := range opts { for _, opt := range opts {
opt(&options) opt(&options)
} }
return &httpPluginIngress{ return &httpPlugin{
url: url, url: url,
client: plugin.NewHTTPClient(&options), client: plugin.NewHTTPClient(&options),
header: options.Header, header: options.Header,
@ -102,12 +102,12 @@ func NewHTTPPluginIngress(name string, url string, opts ...plugin.Option) ingres
} }
} }
func (p *httpPluginIngress) Get(ctx context.Context, host string) (endpoint string) { func (p *httpPlugin) Get(ctx context.Context, host string) (endpoint string) {
if p.client == nil { if p.client == nil {
return return
} }
rb := httpIngressRequest{ rb := httpPluginRequest{
Host: host, Host: host,
} }
v, err := json.Marshal(&rb) v, err := json.Marshal(&rb)
@ -134,7 +134,7 @@ func (p *httpPluginIngress) Get(ctx context.Context, host string) (endpoint stri
return return
} }
res := httpIngressResponse{} res := httpPluginResponse{}
if err := json.NewDecoder(resp.Body).Decode(&res); err != nil { if err := json.NewDecoder(resp.Body).Decode(&res); err != nil {
return return
} }

View File

@ -40,6 +40,47 @@ func KeyRedisLoaderOption(key string) RedisLoaderOption {
} }
} }
type redisStringLoader struct {
client *redis.Client
key string
}
// RedisStringLoader loads data from redis string.
func RedisStringLoader(addr string, opts ...RedisLoaderOption) Loader {
var options redisLoaderOptions
for _, opt := range opts {
if opt != nil {
opt(&options)
}
}
key := options.key
if key == "" {
key = DefaultRedisKey
}
return &redisStringLoader{
client: redis.NewClient(&redis.Options{
Addr: addr,
Password: options.password,
DB: options.db,
}),
key: key,
}
}
func (p *redisStringLoader) Load(ctx context.Context) (io.Reader, error) {
v, err := p.client.Get(ctx, p.key).Bytes()
if err != nil {
return nil, err
}
return bytes.NewReader(v), nil
}
func (p *redisStringLoader) Close() error {
return p.client.Close()
}
type redisSetLoader struct { type redisSetLoader struct {
client *redis.Client client *redis.Client
key string key string

View File

@ -2,6 +2,7 @@ package pht
import ( import (
"bufio" "bufio"
"context"
"crypto/tls" "crypto/tls"
"encoding/base64" "encoding/base64"
"errors" "errors"
@ -38,6 +39,7 @@ type serverOptions struct {
tlsConfig *tls.Config tlsConfig *tls.Config
readBufferSize int readBufferSize int
readTimeout time.Duration readTimeout time.Duration
mptcp bool
logger logger.Logger logger logger.Logger
} }
@ -81,6 +83,12 @@ func ReadTimeoutServerOption(timeout time.Duration) ServerOption {
} }
} }
func MPTCPServerOption(mptcp bool) ServerOption {
return func(opts *serverOptions) {
opts.mptcp = mptcp
}
}
func LoggerServerOption(logger logger.Logger) ServerOption { func LoggerServerOption(logger logger.Logger) ServerOption {
return func(opts *serverOptions) { return func(opts *serverOptions) {
opts.logger = logger opts.logger = logger
@ -187,7 +195,13 @@ func (s *Server) ListenAndServe() error {
if xnet.IsIPv4(s.httpServer.Addr) { if xnet.IsIPv4(s.httpServer.Addr) {
network = "tcp4" network = "tcp4"
} }
ln, err := net.Listen(network, s.httpServer.Addr)
lc := net.ListenConfig{}
if s.options.mptcp {
lc.SetMultipathTCP(true)
s.options.logger.Debugf("mptcp enabled: %v", lc.MultipathTCP())
}
ln, err := lc.Listen(context.Background(), network, s.httpServer.Addr)
if err != nil { if err != nil {
s.options.logger.Error(err) s.options.logger.Error(err)
return err return err

View File

@ -1,6 +1,7 @@
package grpc package grpc
import ( import (
"context"
"net" "net"
"time" "time"
@ -54,7 +55,12 @@ func (l *grpcListener) Init(md md.Metadata) (err error) {
if xnet.IsIPv4(l.options.Addr) { if xnet.IsIPv4(l.options.Addr) {
network = "tcp4" network = "tcp4"
} }
ln, err := net.Listen(network, l.options.Addr) lc := net.ListenConfig{}
if l.md.mptcp {
lc.SetMultipathTCP(true)
l.logger.Debugf("mptcp enabled: %v", lc.MultipathTCP())
}
ln, err := lc.Listen(context.Background(), network, l.options.Addr)
if err != nil { if err != nil {
return return
} }

View File

@ -21,6 +21,7 @@ type metadata struct {
keepaliveTimeout time.Duration keepaliveTimeout time.Duration
keepalivePermitWithoutStream bool keepalivePermitWithoutStream bool
keepaliveMaxConnectionIdle time.Duration keepaliveMaxConnectionIdle time.Duration
mptcp bool
} }
func (l *grpcListener) parseMetadata(md mdata.Metadata) (err error) { func (l *grpcListener) parseMetadata(md mdata.Metadata) (err error) {
@ -49,6 +50,7 @@ func (l *grpcListener) parseMetadata(md mdata.Metadata) (err error) {
l.md.keepalivePermitWithoutStream = mdutil.GetBool(md, "grpc.keepalive.permitWithoutStream", "keepalive.permitWithoutStream") l.md.keepalivePermitWithoutStream = mdutil.GetBool(md, "grpc.keepalive.permitWithoutStream", "keepalive.permitWithoutStream")
l.md.keepaliveMaxConnectionIdle = mdutil.GetDuration(md, "grpc.keepalive.maxConnectionIdle", "keepalive.maxConnectionIdle") l.md.keepaliveMaxConnectionIdle = mdutil.GetDuration(md, "grpc.keepalive.maxConnectionIdle", "keepalive.maxConnectionIdle")
l.md.mptcp = mdutil.GetBool(md, "mptcp")
} }
return return

View File

@ -1,6 +1,7 @@
package h2 package h2
import ( import (
"context"
"crypto/tls" "crypto/tls"
"errors" "errors"
"net" "net"
@ -74,7 +75,12 @@ func (l *h2Listener) Init(md md.Metadata) (err error) {
if xnet.IsIPv4(l.options.Addr) { if xnet.IsIPv4(l.options.Addr) {
network = "tcp4" network = "tcp4"
} }
ln, err := net.Listen(network, l.options.Addr) lc := net.ListenConfig{}
if l.md.mptcp {
lc.SetMultipathTCP(true)
l.logger.Debugf("mptcp enabled: %v", lc.MultipathTCP())
}
ln, err := lc.Listen(context.Background(), network, l.options.Addr)
if err != nil { if err != nil {
return err return err
} }

View File

@ -12,6 +12,7 @@ const (
type metadata struct { type metadata struct {
path string path string
backlog int backlog int
mptcp bool
} }
func (l *h2Listener) parseMetadata(md mdata.Metadata) (err error) { func (l *h2Listener) parseMetadata(md mdata.Metadata) (err error) {
@ -26,5 +27,7 @@ func (l *h2Listener) parseMetadata(md mdata.Metadata) (err error) {
} }
l.md.path = mdutil.GetString(md, path) l.md.path = mdutil.GetString(md, path)
l.md.mptcp = mdutil.GetBool(md, "mptcp")
return return
} }

View File

@ -1,6 +1,7 @@
package http2 package http2
import ( import (
"context"
"crypto/tls" "crypto/tls"
"net" "net"
"net/http" "net/http"
@ -63,7 +64,12 @@ func (l *http2Listener) Init(md md.Metadata) (err error) {
if xnet.IsIPv4(l.options.Addr) { if xnet.IsIPv4(l.options.Addr) {
network = "tcp4" network = "tcp4"
} }
ln, err := net.Listen(network, l.options.Addr) lc := net.ListenConfig{}
if l.md.mptcp {
lc.SetMultipathTCP(true)
l.logger.Debugf("mptcp enabled: %v", lc.MultipathTCP())
}
ln, err := lc.Listen(context.Background(), network, l.options.Addr)
if err != nil { if err != nil {
return err return err
} }

View File

@ -11,6 +11,7 @@ const (
type metadata struct { type metadata struct {
backlog int backlog int
mptcp bool
} }
func (l *http2Listener) parseMetadata(md mdata.Metadata) (err error) { func (l *http2Listener) parseMetadata(md mdata.Metadata) (err error) {
@ -22,5 +23,7 @@ func (l *http2Listener) parseMetadata(md mdata.Metadata) (err error) {
if l.md.backlog <= 0 { if l.md.backlog <= 0 {
l.md.backlog = defaultBacklog l.md.backlog = defaultBacklog
} }
l.md.mptcp = mdutil.GetBool(md, "mptcp")
return return
} }

View File

@ -1,6 +1,7 @@
package mtls package mtls
import ( import (
"context"
"crypto/tls" "crypto/tls"
"net" "net"
"time" "time"
@ -51,7 +52,13 @@ func (l *mtlsListener) Init(md md.Metadata) (err error) {
if xnet.IsIPv4(l.options.Addr) { if xnet.IsIPv4(l.options.Addr) {
network = "tcp4" network = "tcp4"
} }
ln, err := net.Listen(network, l.options.Addr)
lc := net.ListenConfig{}
if l.md.mptcp {
lc.SetMultipathTCP(true)
l.logger.Debugf("mptcp enabled: %v", lc.MultipathTCP())
}
ln, err := lc.Listen(context.Background(), network, l.options.Addr)
if err != nil { if err != nil {
return return
} }

View File

@ -20,6 +20,7 @@ type metadata struct {
muxMaxStreamBuffer int muxMaxStreamBuffer int
backlog int backlog int
mptcp bool
} }
func (l *mtlsListener) parseMetadata(md mdata.Metadata) (err error) { func (l *mtlsListener) parseMetadata(md mdata.Metadata) (err error) {
@ -46,5 +47,7 @@ func (l *mtlsListener) parseMetadata(md mdata.Metadata) (err error) {
l.md.muxMaxReceiveBuffer = mdutil.GetInt(md, muxMaxReceiveBuffer) l.md.muxMaxReceiveBuffer = mdutil.GetInt(md, muxMaxReceiveBuffer)
l.md.muxMaxStreamBuffer = mdutil.GetInt(md, muxMaxStreamBuffer) l.md.muxMaxStreamBuffer = mdutil.GetInt(md, muxMaxStreamBuffer)
l.md.mptcp = mdutil.GetBool(md, "mptcp")
return return
} }

View File

@ -1,6 +1,7 @@
package mws package mws
import ( import (
"context"
"crypto/tls" "crypto/tls"
"net" "net"
"net/http" "net/http"
@ -94,7 +95,13 @@ func (l *mwsListener) Init(md md.Metadata) (err error) {
if xnet.IsIPv4(l.options.Addr) { if xnet.IsIPv4(l.options.Addr) {
network = "tcp4" network = "tcp4"
} }
ln, err := net.Listen(network, l.options.Addr)
lc := net.ListenConfig{}
if l.md.mptcp {
lc.SetMultipathTCP(true)
l.logger.Debugf("mptcp enabled: %v", lc.MultipathTCP())
}
ln, err := lc.Listen(context.Background(), network, l.options.Addr)
if err != nil { if err != nil {
return return
} }

View File

@ -30,6 +30,8 @@ type metadata struct {
muxMaxFrameSize int muxMaxFrameSize int
muxMaxReceiveBuffer int muxMaxReceiveBuffer int
muxMaxStreamBuffer int muxMaxStreamBuffer int
mptcp bool
} }
func (l *mwsListener) parseMetadata(md mdata.Metadata) (err error) { func (l *mwsListener) parseMetadata(md mdata.Metadata) (err error) {
@ -82,5 +84,8 @@ func (l *mwsListener) parseMetadata(md mdata.Metadata) (err error) {
} }
l.md.header = hd l.md.header = hd
} }
l.md.mptcp = mdutil.GetBool(md, "mptcp")
return return
} }

View File

@ -1,6 +1,7 @@
package http package http
import ( import (
"context"
"net" "net"
"time" "time"
@ -48,7 +49,13 @@ func (l *obfsListener) Init(md md.Metadata) (err error) {
if xnet.IsIPv4(l.options.Addr) { if xnet.IsIPv4(l.options.Addr) {
network = "tcp4" network = "tcp4"
} }
ln, err := net.Listen(network, l.options.Addr)
lc := net.ListenConfig{}
if l.md.mptcp {
lc.SetMultipathTCP(true)
l.logger.Debugf("mptcp enabled: %v", lc.MultipathTCP())
}
ln, err := lc.Listen(context.Background(), network, l.options.Addr)
if err != nil { if err != nil {
return return
} }

View File

@ -9,6 +9,7 @@ import (
type metadata struct { type metadata struct {
header http.Header header http.Header
mptcp bool
} }
func (l *obfsListener) parseMetadata(md mdata.Metadata) (err error) { func (l *obfsListener) parseMetadata(md mdata.Metadata) (err error) {
@ -23,5 +24,7 @@ func (l *obfsListener) parseMetadata(md mdata.Metadata) (err error) {
} }
l.md.header = hd l.md.header = hd
} }
l.md.mptcp = mdutil.GetBool(md, "mptcp")
return return
} }

View File

@ -1,6 +1,7 @@
package tls package tls
import ( import (
"context"
"net" "net"
"time" "time"
@ -47,7 +48,13 @@ func (l *obfsListener) Init(md md.Metadata) (err error) {
if xnet.IsIPv4(l.options.Addr) { if xnet.IsIPv4(l.options.Addr) {
network = "tcp4" network = "tcp4"
} }
ln, err := net.Listen(network, l.options.Addr)
lc := net.ListenConfig{}
if l.md.mptcp {
lc.SetMultipathTCP(true)
l.logger.Debugf("mptcp enabled: %v", lc.MultipathTCP())
}
ln, err := lc.Listen(context.Background(), network, l.options.Addr)
if err != nil { if err != nil {
return return
} }

View File

@ -2,11 +2,14 @@ package tls
import ( import (
md "github.com/go-gost/core/metadata" md "github.com/go-gost/core/metadata"
mdutil "github.com/go-gost/core/metadata/util"
) )
type metadata struct { type metadata struct {
mptcp bool
} }
func (l *obfsListener) parseMetadata(md md.Metadata) (err error) { func (l *obfsListener) parseMetadata(md md.Metadata) (err error) {
l.md.mptcp = mdutil.GetBool(md, "mptcp")
return return
} }

View File

@ -74,6 +74,7 @@ func (l *phtListener) Init(md md.Metadata) (err error) {
pht_util.BacklogServerOption(l.md.backlog), pht_util.BacklogServerOption(l.md.backlog),
pht_util.PathServerOption(l.md.authorizePath, l.md.pushPath, l.md.pullPath), pht_util.PathServerOption(l.md.authorizePath, l.md.pushPath, l.md.pullPath),
pht_util.LoggerServerOption(l.options.Logger), pht_util.LoggerServerOption(l.options.Logger),
pht_util.MPTCPServerOption(l.md.mptcp),
) )
go func() { go func() {

View File

@ -19,6 +19,7 @@ type metadata struct {
pushPath string pushPath string
pullPath string pullPath string
backlog int backlog int
mptcp bool
} }
func (l *phtListener) parseMetadata(md mdata.Metadata) (err error) { func (l *phtListener) parseMetadata(md mdata.Metadata) (err error) {
@ -48,5 +49,6 @@ func (l *phtListener) parseMetadata(md mdata.Metadata) (err error) {
l.md.backlog = defaultBacklog l.md.backlog = defaultBacklog
} }
l.md.mptcp = mdutil.GetBool(md, "mptcp")
return return
} }

View File

@ -46,13 +46,17 @@ func (l *redirectListener) Init(md md.Metadata) (err error) {
return return
} }
network := "tcp"
if xnet.IsIPv4(l.options.Addr) {
network = "tcp4"
}
lc := net.ListenConfig{} lc := net.ListenConfig{}
if l.md.tproxy { if l.md.tproxy {
lc.Control = l.control lc.Control = l.control
} }
network := "tcp" if l.md.mptcp {
if xnet.IsIPv4(l.options.Addr) { lc.SetMultipathTCP(true)
network = "tcp4" l.logger.Debugf("mptcp enabled: %v", lc.MultipathTCP())
} }
ln, err := lc.Listen(context.Background(), network, l.options.Addr) ln, err := lc.Listen(context.Background(), network, l.options.Addr)
if err != nil { if err != nil {

View File

@ -7,6 +7,7 @@ import (
type metadata struct { type metadata struct {
tproxy bool tproxy bool
mptcp bool
} }
func (l *redirectListener) parseMetadata(md mdata.Metadata) (err error) { func (l *redirectListener) parseMetadata(md mdata.Metadata) (err error) {
@ -14,5 +15,6 @@ func (l *redirectListener) parseMetadata(md mdata.Metadata) (err error) {
tproxy = "tproxy" tproxy = "tproxy"
) )
l.md.tproxy = mdutil.GetBool(md, tproxy) l.md.tproxy = mdutil.GetBool(md, tproxy)
l.md.mptcp = mdutil.GetBool(md, "mptcp")
return return
} }

View File

@ -1,6 +1,7 @@
package ssh package ssh
import ( import (
"context"
"fmt" "fmt"
"net" "net"
"time" "time"
@ -53,7 +54,13 @@ func (l *sshListener) Init(md md.Metadata) (err error) {
if xnet.IsIPv4(l.options.Addr) { if xnet.IsIPv4(l.options.Addr) {
network = "tcp4" network = "tcp4"
} }
ln, err := net.Listen(network, l.options.Addr)
lc := net.ListenConfig{}
if l.md.mptcp {
lc.SetMultipathTCP(true)
l.logger.Debugf("mptcp enabled: %v", lc.MultipathTCP())
}
ln, err := lc.Listen(context.Background(), network, l.options.Addr)
if err != nil { if err != nil {
return err return err
} }

View File

@ -17,6 +17,7 @@ type metadata struct {
signer ssh.Signer signer ssh.Signer
authorizedKeys map[string]bool authorizedKeys map[string]bool
backlog int backlog int
mptcp bool
} }
func (l *sshListener) parseMetadata(md mdata.Metadata) (err error) { func (l *sshListener) parseMetadata(md mdata.Metadata) (err error) {
@ -64,5 +65,6 @@ func (l *sshListener) parseMetadata(md mdata.Metadata) (err error) {
l.md.backlog = defaultBacklog l.md.backlog = defaultBacklog
} }
l.md.mptcp = mdutil.GetBool(md, "mptcp")
return return
} }

View File

@ -62,7 +62,13 @@ func (l *sshdListener) Init(md md.Metadata) (err error) {
if xnet.IsIPv4(l.options.Addr) { if xnet.IsIPv4(l.options.Addr) {
network = "tcp4" network = "tcp4"
} }
ln, err := net.Listen(network, l.options.Addr)
lc := net.ListenConfig{}
if l.md.mptcp {
lc.SetMultipathTCP(true)
l.logger.Debugf("mptcp enabled: %v", lc.MultipathTCP())
}
ln, err := lc.Listen(context.Background(), network, l.options.Addr)
if err != nil { if err != nil {
return err return err
} }

View File

@ -17,6 +17,7 @@ type metadata struct {
signer ssh.Signer signer ssh.Signer
authorizedKeys map[string]bool authorizedKeys map[string]bool
backlog int backlog int
mptcp bool
} }
func (l *sshdListener) parseMetadata(md mdata.Metadata) (err error) { func (l *sshdListener) parseMetadata(md mdata.Metadata) (err error) {
@ -64,5 +65,6 @@ func (l *sshdListener) parseMetadata(md mdata.Metadata) (err error) {
l.md.backlog = defaultBacklog l.md.backlog = defaultBacklog
} }
l.md.mptcp = mdutil.GetBool(md, "mptcp")
return return
} }

View File

@ -1,6 +1,7 @@
package tcp package tcp
import ( import (
"context"
"net" "net"
"time" "time"
@ -47,7 +48,13 @@ func (l *tcpListener) Init(md md.Metadata) (err error) {
if xnet.IsIPv4(l.options.Addr) { if xnet.IsIPv4(l.options.Addr) {
network = "tcp4" network = "tcp4"
} }
ln, err := net.Listen(network, l.options.Addr)
lc := net.ListenConfig{}
if l.md.mptcp {
lc.SetMultipathTCP(true)
l.logger.Debugf("mptcp enabled: %v", lc.MultipathTCP())
}
ln, err := lc.Listen(context.Background(), network, l.options.Addr)
if err != nil { if err != nil {
return return
} }

View File

@ -2,11 +2,14 @@ package tcp
import ( import (
md "github.com/go-gost/core/metadata" md "github.com/go-gost/core/metadata"
mdutil "github.com/go-gost/core/metadata/util"
) )
type metadata struct { type metadata struct {
mptcp bool
} }
func (l *tcpListener) parseMetadata(md md.Metadata) (err error) { func (l *tcpListener) parseMetadata(md md.Metadata) (err error) {
l.md.mptcp = mdutil.GetBool(md, "mptcp")
return return
} }

View File

@ -1,6 +1,7 @@
package tls package tls
import ( import (
"context"
"crypto/tls" "crypto/tls"
"net" "net"
"time" "time"
@ -48,7 +49,13 @@ func (l *tlsListener) Init(md md.Metadata) (err error) {
if xnet.IsIPv4(l.options.Addr) { if xnet.IsIPv4(l.options.Addr) {
network = "tcp4" network = "tcp4"
} }
ln, err := net.Listen(network, l.options.Addr)
lc := net.ListenConfig{}
if l.md.mptcp {
lc.SetMultipathTCP(true)
l.logger.Debugf("mptcp enabled: %v", lc.MultipathTCP())
}
ln, err := lc.Listen(context.Background(), network, l.options.Addr)
if err != nil { if err != nil {
return return
} }

View File

@ -2,11 +2,14 @@ package tls
import ( import (
mdata "github.com/go-gost/core/metadata" mdata "github.com/go-gost/core/metadata"
mdutil "github.com/go-gost/core/metadata/util"
) )
type metadata struct { type metadata struct {
mptcp bool
} }
func (l *tlsListener) parseMetadata(md mdata.Metadata) (err error) { func (l *tlsListener) parseMetadata(md mdata.Metadata) (err error) {
l.md.mptcp = mdutil.GetBool(md, "mptcp")
return return
} }

View File

@ -1,6 +1,7 @@
package ws package ws
import ( import (
"context"
"crypto/tls" "crypto/tls"
"net" "net"
"net/http" "net/http"
@ -89,7 +90,13 @@ func (l *wsListener) Init(md md.Metadata) (err error) {
if xnet.IsIPv4(l.options.Addr) { if xnet.IsIPv4(l.options.Addr) {
network = "tcp4" network = "tcp4"
} }
ln, err := net.Listen(network, l.options.Addr)
lc := net.ListenConfig{}
if l.md.mptcp {
lc.SetMultipathTCP(true)
l.logger.Debugf("mptcp enabled: %v", lc.MultipathTCP())
}
ln, err := lc.Listen(context.Background(), network, l.options.Addr)
if err != nil { if err != nil {
return return
} }

View File

@ -22,8 +22,9 @@ type metadata struct {
readBufferSize int readBufferSize int
writeBufferSize int writeBufferSize int
enableCompression bool enableCompression bool
header http.Header
header http.Header mptcp bool
} }
func (l *wsListener) parseMetadata(md mdata.Metadata) (err error) { func (l *wsListener) parseMetadata(md mdata.Metadata) (err error) {
@ -63,5 +64,7 @@ func (l *wsListener) parseMetadata(md mdata.Metadata) (err error) {
} }
l.md.header = hd l.md.header = hd
} }
l.md.mptcp = mdutil.GetBool(md, "mptcp")
return return
} }

View File

@ -12,18 +12,18 @@ import (
"github.com/go-gost/core/logger" "github.com/go-gost/core/logger"
"github.com/go-gost/core/recorder" "github.com/go-gost/core/recorder"
"github.com/go-gost/plugin/recorder/proto" "github.com/go-gost/plugin/recorder/proto"
"github.com/go-gost/x/internal/util/plugin" "github.com/go-gost/x/internal/plugin"
"google.golang.org/grpc" "google.golang.org/grpc"
) )
type grpcPluginRecorder struct { type grpcPlugin struct {
conn grpc.ClientConnInterface conn grpc.ClientConnInterface
client proto.RecorderClient client proto.RecorderClient
log logger.Logger log logger.Logger
} }
// NewGRPCPluginRecorder creates a Recorder plugin based on gRPC. // NewGRPCPlugin creates a Recorder plugin based on gRPC.
func NewGRPCPluginRecorder(name string, addr string, opts ...plugin.Option) recorder.Recorder { func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) recorder.Recorder {
var options plugin.Options var options plugin.Options
for _, opt := range opts { for _, opt := range opts {
opt(&options) opt(&options)
@ -38,7 +38,7 @@ func NewGRPCPluginRecorder(name string, addr string, opts ...plugin.Option) reco
log.Error(err) log.Error(err)
} }
p := &grpcPluginRecorder{ p := &grpcPlugin{
conn: conn, conn: conn,
log: log, log: log,
} }
@ -48,7 +48,7 @@ func NewGRPCPluginRecorder(name string, addr string, opts ...plugin.Option) reco
return p return p
} }
func (p *grpcPluginRecorder) Record(ctx context.Context, b []byte) error { func (p *grpcPlugin) Record(ctx context.Context, b []byte) error {
if p.client == nil { if p.client == nil {
return nil return nil
} }
@ -64,36 +64,36 @@ func (p *grpcPluginRecorder) Record(ctx context.Context, b []byte) error {
return nil return nil
} }
func (p *grpcPluginRecorder) Close() error { func (p *grpcPlugin) Close() error {
if closer, ok := p.conn.(io.Closer); ok { if closer, ok := p.conn.(io.Closer); ok {
return closer.Close() return closer.Close()
} }
return nil return nil
} }
type httpRecorderRequest struct { type httpPluginRequest struct {
Data []byte `json:"data"` Data []byte `json:"data"`
} }
type httpRecorderResponse struct { type httpPluginResponse struct {
OK bool `json:"ok"` OK bool `json:"ok"`
} }
type httpPluginRecorder struct { type httpPlugin struct {
url string url string
client *http.Client client *http.Client
header http.Header header http.Header
log logger.Logger log logger.Logger
} }
// NewHTTPPluginRecorder creates an Recorder plugin based on HTTP. // NewHTTPPlugin creates an Recorder plugin based on HTTP.
func NewHTTPPluginRecorder(name string, url string, opts ...plugin.Option) recorder.Recorder { func NewHTTPPlugin(name string, url string, opts ...plugin.Option) recorder.Recorder {
var options plugin.Options var options plugin.Options
for _, opt := range opts { for _, opt := range opts {
opt(&options) opt(&options)
} }
return &httpPluginRecorder{ return &httpPlugin{
url: url, url: url,
client: plugin.NewHTTPClient(&options), client: plugin.NewHTTPClient(&options),
header: options.Header, header: options.Header,
@ -104,12 +104,12 @@ func NewHTTPPluginRecorder(name string, url string, opts ...plugin.Option) recor
} }
} }
func (p *httpPluginRecorder) Record(ctx context.Context, b []byte) error { func (p *httpPlugin) Record(ctx context.Context, b []byte) error {
if len(b) == 0 || p.client == nil { if len(b) == 0 || p.client == nil {
return nil return nil
} }
rb := httpRecorderRequest{ rb := httpPluginRequest{
Data: b, Data: b,
} }
v, err := json.Marshal(&rb) v, err := json.Marshal(&rb)
@ -136,7 +136,7 @@ func (p *httpPluginRecorder) Record(ctx context.Context, b []byte) error {
return fmt.Errorf("%s", resp.Status) return fmt.Errorf("%s", resp.Status)
} }
res := httpRecorderResponse{} res := httpPluginResponse{}
if err := json.NewDecoder(resp.Body).Decode(&res); err != nil { if err := json.NewDecoder(resp.Body).Decode(&res); err != nil {
return err return err
} }

View File

@ -4,24 +4,25 @@ import (
"context" "context"
"github.com/go-gost/core/chain" "github.com/go-gost/core/chain"
"github.com/go-gost/core/hop"
) )
type hopRegistry struct { type hopRegistry struct {
registry[chain.Hop] registry[hop.Hop]
} }
func (r *hopRegistry) Register(name string, v chain.Hop) error { func (r *hopRegistry) Register(name string, v hop.Hop) error {
return r.registry.Register(name, v) return r.registry.Register(name, v)
} }
func (r *hopRegistry) Get(name string) chain.Hop { func (r *hopRegistry) Get(name string) hop.Hop {
if name != "" { if name != "" {
return &hopWrapper{name: name, r: r} return &hopWrapper{name: name, r: r}
} }
return nil return nil
} }
func (r *hopRegistry) get(name string) chain.Hop { func (r *hopRegistry) get(name string) hop.Hop {
return r.registry.Get(name) return r.registry.Get(name)
} }
@ -35,10 +36,13 @@ func (w *hopWrapper) Nodes() []*chain.Node {
if v == nil { if v == nil {
return nil return nil
} }
return v.Nodes() if nl, ok := v.(hop.NodeList); ok {
return nl.Nodes()
}
return nil
} }
func (w *hopWrapper) Select(ctx context.Context, opts ...chain.SelectOption) *chain.Node { func (w *hopWrapper) Select(ctx context.Context, opts ...hop.SelectOption) *chain.Node {
v := w.r.get(w.name) v := w.r.get(w.name)
if v == nil { if v == nil {
return nil return nil

View File

@ -9,6 +9,7 @@ import (
"github.com/go-gost/core/auth" "github.com/go-gost/core/auth"
"github.com/go-gost/core/bypass" "github.com/go-gost/core/bypass"
"github.com/go-gost/core/chain" "github.com/go-gost/core/chain"
"github.com/go-gost/core/hop"
"github.com/go-gost/core/hosts" "github.com/go-gost/core/hosts"
"github.com/go-gost/core/ingress" "github.com/go-gost/core/ingress"
"github.com/go-gost/core/limiter/conn" "github.com/go-gost/core/limiter/conn"
@ -31,7 +32,7 @@ var (
connectorReg reg.Registry[NewConnector] = new(connectorRegistry) connectorReg reg.Registry[NewConnector] = new(connectorRegistry)
serviceReg reg.Registry[service.Service] = new(serviceRegistry) serviceReg reg.Registry[service.Service] = new(serviceRegistry)
chainReg reg.Registry[chain.Chainer] = new(chainRegistry) chainReg reg.Registry[chain.Chainer] = new(chainRegistry)
hopReg reg.Registry[chain.Hop] = new(hopRegistry) hopReg reg.Registry[hop.Hop] = new(hopRegistry)
autherReg reg.Registry[auth.Authenticator] = new(autherRegistry) autherReg reg.Registry[auth.Authenticator] = new(autherRegistry)
admissionReg reg.Registry[admission.Admission] = new(admissionRegistry) admissionReg reg.Registry[admission.Admission] = new(admissionRegistry)
bypassReg reg.Registry[bypass.Bypass] = new(bypassRegistry) bypassReg reg.Registry[bypass.Bypass] = new(bypassRegistry)
@ -119,7 +120,7 @@ func ChainRegistry() reg.Registry[chain.Chainer] {
return chainReg return chainReg
} }
func HopRegistry() reg.Registry[chain.Hop] { func HopRegistry() reg.Registry[hop.Hop] {
return hopReg return hopReg
} }

View File

@ -14,18 +14,18 @@ import (
"github.com/go-gost/core/resolver" "github.com/go-gost/core/resolver"
"github.com/go-gost/plugin/resolver/proto" "github.com/go-gost/plugin/resolver/proto"
auth_util "github.com/go-gost/x/internal/util/auth" auth_util "github.com/go-gost/x/internal/util/auth"
"github.com/go-gost/x/internal/util/plugin" "github.com/go-gost/x/internal/plugin"
"google.golang.org/grpc" "google.golang.org/grpc"
) )
type grpcPluginResolver struct { type grpcPlugin struct {
conn grpc.ClientConnInterface conn grpc.ClientConnInterface
client proto.ResolverClient client proto.ResolverClient
log logger.Logger log logger.Logger
} }
// NewGRPCPluginResolver creates a Resolver plugin based on gRPC. // NewGRPCPlugin creates a Resolver plugin based on gRPC.
func NewGRPCPluginResolver(name string, addr string, opts ...plugin.Option) (resolver.Resolver, error) { func NewGRPCPlugin(name string, addr string, opts ...plugin.Option) (resolver.Resolver, error) {
var options plugin.Options var options plugin.Options
for _, opt := range opts { for _, opt := range opts {
opt(&options) opt(&options)
@ -39,7 +39,7 @@ func NewGRPCPluginResolver(name string, addr string, opts ...plugin.Option) (res
if err != nil { if err != nil {
log.Error(err) log.Error(err)
} }
p := &grpcPluginResolver{ p := &grpcPlugin{
conn: conn, conn: conn,
log: log, log: log,
} }
@ -49,7 +49,7 @@ func NewGRPCPluginResolver(name string, addr string, opts ...plugin.Option) (res
return p, nil return p, nil
} }
func (p *grpcPluginResolver) Resolve(ctx context.Context, network, host string) (ips []net.IP, err error) { func (p *grpcPlugin) Resolve(ctx context.Context, network, host string) (ips []net.IP, err error) {
p.log.Debugf("resolve %s/%s", host, network) p.log.Debugf("resolve %s/%s", host, network)
if p.client == nil { if p.client == nil {
@ -74,39 +74,39 @@ func (p *grpcPluginResolver) Resolve(ctx context.Context, network, host string)
return return
} }
func (p *grpcPluginResolver) Close() error { func (p *grpcPlugin) Close() error {
if closer, ok := p.conn.(io.Closer); ok { if closer, ok := p.conn.(io.Closer); ok {
return closer.Close() return closer.Close()
} }
return nil return nil
} }
type httpResolverRequest struct { type httpPluginRequest struct {
Network string `json:"network"` Network string `json:"network"`
Host string `json:"host"` Host string `json:"host"`
Client string `json:"client"` Client string `json:"client"`
} }
type httpResolverResponse struct { type httpPluginResponse struct {
IPs []string `json:"ips"` IPs []string `json:"ips"`
OK bool `json:"ok"` OK bool `json:"ok"`
} }
type httpPluginResolver struct { type httpPlugin struct {
url string url string
client *http.Client client *http.Client
header http.Header header http.Header
log logger.Logger log logger.Logger
} }
// NewHTTPPluginResolver creates an Resolver plugin based on HTTP. // NewHTTPPlugin creates an Resolver plugin based on HTTP.
func NewHTTPPluginResolver(name string, url string, opts ...plugin.Option) resolver.Resolver { func NewHTTPPlugin(name string, url string, opts ...plugin.Option) resolver.Resolver {
var options plugin.Options var options plugin.Options
for _, opt := range opts { for _, opt := range opts {
opt(&options) opt(&options)
} }
return &httpPluginResolver{ return &httpPlugin{
url: url, url: url,
client: plugin.NewHTTPClient(&options), client: plugin.NewHTTPClient(&options),
header: options.Header, header: options.Header,
@ -117,14 +117,14 @@ func NewHTTPPluginResolver(name string, url string, opts ...plugin.Option) resol
} }
} }
func (p *httpPluginResolver) Resolve(ctx context.Context, network, host string) (ips []net.IP, err error) { func (p *httpPlugin) Resolve(ctx context.Context, network, host string) (ips []net.IP, err error) {
p.log.Debugf("resolve %s/%s", host, network) p.log.Debugf("resolve %s/%s", host, network)
if p.client == nil { if p.client == nil {
return return
} }
rb := httpResolverRequest{ rb := httpPluginRequest{
Network: network, Network: network,
Host: host, Host: host,
Client: string(auth_util.IDFromContext(ctx)), Client: string(auth_util.IDFromContext(ctx)),
@ -154,7 +154,7 @@ func (p *httpPluginResolver) Resolve(ctx context.Context, network, host string)
return return
} }
res := httpResolverResponse{} res := httpPluginResponse{}
if err = json.NewDecoder(resp.Body).Decode(&res); err != nil { if err = json.NewDecoder(resp.Body).Decode(&res); err != nil {
return return
} }

View File

@ -12,7 +12,6 @@ import (
resolver_util "github.com/go-gost/x/internal/util/resolver" resolver_util "github.com/go-gost/x/internal/util/resolver"
"github.com/go-gost/x/resolver/exchanger" "github.com/go-gost/x/resolver/exchanger"
"github.com/miekg/dns" "github.com/miekg/dns"
"google.golang.org/grpc"
) )
type NameServer struct { type NameServer struct {
@ -28,7 +27,6 @@ type NameServer struct {
type options struct { type options struct {
domain string domain string
client *grpc.ClientConn
logger logger.Logger logger logger.Logger
} }
@ -40,12 +38,6 @@ func DomainOption(domain string) Option {
} }
} }
func PluginConnOption(c *grpc.ClientConn) Option {
return func(opts *options) {
opts.client = c
}
}
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