mirror of
https://github.com/go-gost/gost.git
synced 2026-10-08 20:15:46 +00:00
Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9db7137cd3 | ||
|
|
307a90c20e | ||
|
|
5daefb8e3c | ||
|
|
edca3e0a55 | ||
|
|
a8804ea02d | ||
|
|
f2d806886a | ||
|
|
fdd67a6086 | ||
|
|
345338d410 | ||
|
|
a8a6bbc3a3 | ||
|
|
c1bf501734 | ||
|
|
f1eaef8d69 | ||
|
|
0fba6d2500 | ||
|
|
1ec10ff7dd | ||
|
|
0983ecc52f |
@@ -36,7 +36,7 @@ jobs:
|
||||
# Set output parameters.
|
||||
echo ::set-output name=tags::${TAGS}
|
||||
echo ::set-output name=docker_image::${DOCKER_IMAGE}
|
||||
echo ::set-output name=docker_platforms::linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8
|
||||
echo ::set-output name=docker_platforms::linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/ppc64le,linux/s390x
|
||||
|
||||
# https://github.com/crazy-max/ghaction-docker-buildx
|
||||
- name: Set up Docker Buildx
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
FROM --platform=$BUILDPLATFORM golang:1-alpine as builder
|
||||
FROM --platform=$BUILDPLATFORM golang:1.17.7-alpine as builder
|
||||
# FROM --platform=$BUILDPLATFORM golang:1.18-rc-alpine as builder
|
||||
|
||||
# Convert TARGETPLATFORM to GOARCH format
|
||||
# https://github.com/tonistiigi/xx
|
||||
|
||||
@@ -2,7 +2,7 @@ NAME=gost
|
||||
BINDIR=bin
|
||||
VERSION=$(shell cat cmd/gost/version.go | grep 'version =' | sed 's/.*\"\(.*\)\".*/\1/g')
|
||||
GOBUILD=CGO_ENABLED=0 go build --ldflags="-s -w" -v -x -a
|
||||
GOFILES=cmd/gost/*
|
||||
GOFILES=cmd/gost/*.go
|
||||
|
||||
PLATFORM_LIST = \
|
||||
darwin-amd64 \
|
||||
@@ -19,6 +19,7 @@ PLATFORM_LIST = \
|
||||
linux-mipsle-hardfloat \
|
||||
linux-mips64 \
|
||||
linux-mips64le \
|
||||
linux-s390x \
|
||||
freebsd-386 \
|
||||
freebsd-amd64
|
||||
|
||||
@@ -26,7 +27,7 @@ WINDOWS_ARCH_LIST = \
|
||||
windows-386 \
|
||||
windows-amd64
|
||||
|
||||
all: linux-amd64 darwin-amd64 windows-amd64 # Most used
|
||||
all: linux-amd64 darwin-amd64 darwin-arm64 windows-amd64 # Most used
|
||||
|
||||
darwin-amd64:
|
||||
GOARCH=amd64 GOOS=darwin $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)
|
||||
@@ -70,6 +71,9 @@ linux-mips64:
|
||||
linux-mips64le:
|
||||
GOARCH=mips64le GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)
|
||||
|
||||
linux-s390x:
|
||||
GOARCH=s390x GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)
|
||||
|
||||
freebsd-386:
|
||||
GOARCH=386 GOOS=freebsd $(GOBUILD) -o $(BINDIR)/$(NAME)-$@ $(GOFILES)
|
||||
|
||||
|
||||
+42
-14
@@ -34,8 +34,15 @@ func buildConfigFromCmd(services, nodes stringList) (*config.Config, error) {
|
||||
|
||||
if v := os.Getenv("GOST_PROFILING"); v != "" {
|
||||
cfg.Profiling = &config.ProfilingConfig{
|
||||
Addr: v,
|
||||
Enabled: true,
|
||||
Addr: v,
|
||||
Enable: true,
|
||||
}
|
||||
}
|
||||
if v := os.Getenv("GOST_METRICS"); v != "" {
|
||||
cfg.Metrics = &config.MetricsConfig{
|
||||
Addr: v,
|
||||
Path: "/metrics",
|
||||
Enable: true,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +114,7 @@ func buildConfigFromCmd(services, nodes stringList) (*config.Config, error) {
|
||||
}
|
||||
resolverCfg.Nameservers = append(
|
||||
resolverCfg.Nameservers,
|
||||
config.NameserverConfig{
|
||||
&config.NameserverConfig{
|
||||
Addr: rs,
|
||||
},
|
||||
)
|
||||
@@ -127,7 +134,7 @@ func buildConfigFromCmd(services, nodes stringList) (*config.Config, error) {
|
||||
}
|
||||
hostsCfg.Mappings = append(
|
||||
hostsCfg.Mappings,
|
||||
config.HostMappingConfig{
|
||||
&config.HostMappingConfig{
|
||||
Hostname: ss[0],
|
||||
IP: ss[1],
|
||||
},
|
||||
@@ -166,6 +173,24 @@ func buildConfigFromCmd(services, nodes stringList) (*config.Config, error) {
|
||||
service.Handler.Retries = v
|
||||
md.Del("retries")
|
||||
}
|
||||
if v := metadata.GetString(md, "admission"); v != "" {
|
||||
admCfg := &config.AdmissionConfig{
|
||||
Name: fmt.Sprintf("admission-%d", len(cfg.Admissions)),
|
||||
}
|
||||
if v[0] == '~' {
|
||||
admCfg.Reverse = true
|
||||
v = v[1:]
|
||||
}
|
||||
for _, s := range strings.Split(v, ",") {
|
||||
if s == "" {
|
||||
continue
|
||||
}
|
||||
admCfg.Matchers = append(admCfg.Matchers, s)
|
||||
}
|
||||
service.Admission = admCfg.Name
|
||||
cfg.Admissions = append(cfg.Admissions, admCfg)
|
||||
md.Del("admission")
|
||||
}
|
||||
if v := metadata.GetString(md, "bypass"); v != "" {
|
||||
bypassCfg := &config.BypassConfig{
|
||||
Name: fmt.Sprintf("bypass-%d", len(cfg.Bypasses)),
|
||||
@@ -194,7 +219,7 @@ func buildConfigFromCmd(services, nodes stringList) (*config.Config, error) {
|
||||
}
|
||||
resolverCfg.Nameservers = append(
|
||||
resolverCfg.Nameservers,
|
||||
config.NameserverConfig{
|
||||
&config.NameserverConfig{
|
||||
Addr: rs,
|
||||
},
|
||||
)
|
||||
@@ -214,7 +239,7 @@ func buildConfigFromCmd(services, nodes stringList) (*config.Config, error) {
|
||||
}
|
||||
hostsCfg.Mappings = append(
|
||||
hostsCfg.Mappings,
|
||||
config.HostMappingConfig{
|
||||
&config.HostMappingConfig{
|
||||
Hostname: ss[0],
|
||||
IP: ss[1],
|
||||
},
|
||||
@@ -271,13 +296,12 @@ func buildServiceConfig(url *url.URL) (*config.ServiceConfig, error) {
|
||||
}
|
||||
}
|
||||
|
||||
var auths []*config.AuthConfig
|
||||
var auth *config.AuthConfig
|
||||
if url.User != nil {
|
||||
auth := &config.AuthConfig{
|
||||
auth = &config.AuthConfig{
|
||||
Username: url.User.Username(),
|
||||
}
|
||||
auth.Password, _ = url.User.Password()
|
||||
auths = append(auths, auth)
|
||||
}
|
||||
|
||||
md := metadata.MapMetadata{}
|
||||
@@ -292,7 +316,7 @@ func buildServiceConfig(url *url.URL) (*config.ServiceConfig, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
auths = append(auths, au)
|
||||
auth = au
|
||||
}
|
||||
md.Del("auth")
|
||||
|
||||
@@ -319,7 +343,7 @@ func buildServiceConfig(url *url.URL) (*config.ServiceConfig, error) {
|
||||
|
||||
svc.Handler = &config.HandlerConfig{
|
||||
Type: handler,
|
||||
Auths: auths,
|
||||
Auth: auth,
|
||||
Metadata: md,
|
||||
}
|
||||
svc.Listener = &config.ListenerConfig{
|
||||
@@ -329,10 +353,10 @@ func buildServiceConfig(url *url.URL) (*config.ServiceConfig, error) {
|
||||
}
|
||||
|
||||
if svc.Handler.Type == "sshd" {
|
||||
svc.Handler.Auths = nil
|
||||
svc.Handler.Auth = nil
|
||||
}
|
||||
if svc.Listener.Type == "sshd" {
|
||||
svc.Listener.Auths = auths
|
||||
svc.Listener.Auth = auth
|
||||
}
|
||||
|
||||
return svc, nil
|
||||
@@ -389,6 +413,8 @@ func buildNodeConfig(url *url.URL) (*config.NodeConfig, error) {
|
||||
md.Del("auth")
|
||||
|
||||
tlsConfig := &config.TLSConfig{
|
||||
CertFile: metadata.GetString(md, "certFile"),
|
||||
KeyFile: metadata.GetString(md, "keyFile"),
|
||||
CAFile: metadata.GetString(md, "caFile"),
|
||||
Secure: metadata.GetBool(md, "secure"),
|
||||
ServerName: metadata.GetString(md, "serverName"),
|
||||
@@ -396,11 +422,13 @@ func buildNodeConfig(url *url.URL) (*config.NodeConfig, error) {
|
||||
if tlsConfig.ServerName == "" {
|
||||
tlsConfig.ServerName = url.Hostname()
|
||||
}
|
||||
md.Del("certFile")
|
||||
md.Del("keyFile")
|
||||
md.Del("caFile")
|
||||
md.Del("secure")
|
||||
md.Del("serverName")
|
||||
|
||||
if !tlsConfig.Secure && tlsConfig.CAFile == "" {
|
||||
if !tlsConfig.Secure && tlsConfig.CertFile == "" && tlsConfig.CAFile == "" {
|
||||
tlsConfig = nil
|
||||
}
|
||||
|
||||
|
||||
+63
-366
@@ -1,308 +1,94 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/go-gost/gost/pkg/bypass"
|
||||
"github.com/go-gost/gost/pkg/chain"
|
||||
"github.com/go-gost/gost/pkg/api"
|
||||
"github.com/go-gost/gost/pkg/config"
|
||||
"github.com/go-gost/gost/pkg/connector"
|
||||
"github.com/go-gost/gost/pkg/dialer"
|
||||
"github.com/go-gost/gost/pkg/handler"
|
||||
hostspkg "github.com/go-gost/gost/pkg/hosts"
|
||||
"github.com/go-gost/gost/pkg/listener"
|
||||
"github.com/go-gost/gost/pkg/config/parsing"
|
||||
"github.com/go-gost/gost/pkg/logger"
|
||||
"github.com/go-gost/gost/pkg/metadata"
|
||||
"github.com/go-gost/gost/pkg/registry"
|
||||
"github.com/go-gost/gost/pkg/resolver"
|
||||
resolver_impl "github.com/go-gost/gost/pkg/resolver/impl"
|
||||
"github.com/go-gost/gost/pkg/service"
|
||||
)
|
||||
|
||||
var (
|
||||
chains = make(map[string]*chain.Chain)
|
||||
bypasses = make(map[string]bypass.Bypass)
|
||||
resolvers = make(map[string]resolver.Resolver)
|
||||
hosts = make(map[string]hostspkg.HostMapper)
|
||||
)
|
||||
|
||||
func buildService(cfg *config.Config) (services []*service.Service) {
|
||||
if cfg == nil || len(cfg.Services) == 0 {
|
||||
func buildService(cfg *config.Config) (services []service.Service) {
|
||||
if cfg == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, autherCfg := range cfg.Authers {
|
||||
if auther := parsing.ParseAuther(autherCfg); auther != nil {
|
||||
if err := registry.Auther().Register(autherCfg.Name, auther); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, admissionCfg := range cfg.Admissions {
|
||||
if adm := parsing.ParseAdmission(admissionCfg); adm != nil {
|
||||
if err := registry.Admission().Register(admissionCfg.Name, adm); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, bypassCfg := range cfg.Bypasses {
|
||||
bypasses[bypassCfg.Name] = bypassFromConfig(bypassCfg)
|
||||
if bp := parsing.ParseBypass(bypassCfg); bp != nil {
|
||||
if err := registry.Bypass().Register(bypassCfg.Name, bp); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, resolverCfg := range cfg.Resolvers {
|
||||
r, err := resolverFromConfig(resolverCfg)
|
||||
r, err := parsing.ParseResolver(resolverCfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
resolvers[resolverCfg.Name] = r
|
||||
if r != nil {
|
||||
if err := registry.Resolver().Register(resolverCfg.Name, r); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, hostsCfg := range cfg.Hosts {
|
||||
hosts[hostsCfg.Name] = hostsFromConfig(hostsCfg)
|
||||
if h := parsing.ParseHosts(hostsCfg); h != nil {
|
||||
if err := registry.Hosts().Register(hostsCfg.Name, h); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, chainCfg := range cfg.Chains {
|
||||
chains[chainCfg.Name] = chainFromConfig(chainCfg)
|
||||
c, err := parsing.ParseChain(chainCfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if c != nil {
|
||||
if err := registry.Chain().Register(chainCfg.Name, c); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, svc := range cfg.Services {
|
||||
if svc.Listener == nil {
|
||||
svc.Listener = &config.ListenerConfig{
|
||||
Type: "tcp",
|
||||
}
|
||||
}
|
||||
if svc.Handler == nil {
|
||||
svc.Handler = &config.HandlerConfig{
|
||||
Type: "auto",
|
||||
}
|
||||
}
|
||||
serviceLogger := log.WithFields(map[string]interface{}{
|
||||
"kind": "service",
|
||||
"service": svc.Name,
|
||||
"listener": svc.Listener.Type,
|
||||
"handler": svc.Handler.Type,
|
||||
})
|
||||
|
||||
listenerLogger := serviceLogger.WithFields(map[string]interface{}{
|
||||
"kind": "listener",
|
||||
})
|
||||
|
||||
var tlsConfig *tls.Config
|
||||
var err error
|
||||
|
||||
tlsCfg := svc.Listener.TLS
|
||||
if tlsCfg == nil {
|
||||
tlsCfg = &config.TLSConfig{}
|
||||
}
|
||||
tlsConfig, err = loadServerTLSConfig(tlsCfg)
|
||||
for _, svcCfg := range cfg.Services {
|
||||
svc, err := parsing.ParseService(svcCfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
ln := registry.GetListener(svc.Listener.Type)(
|
||||
listener.AddrOption(svc.Addr),
|
||||
listener.ChainOption(chains[svc.Listener.Chain]),
|
||||
listener.AuthsOption(authsFromConfig(svc.Listener.Auths...)...),
|
||||
listener.TLSConfigOption(tlsConfig),
|
||||
listener.LoggerOption(listenerLogger),
|
||||
)
|
||||
|
||||
if svc.Listener.Metadata == nil {
|
||||
svc.Listener.Metadata = make(map[string]interface{})
|
||||
if svc != nil {
|
||||
if err := registry.Service().Register(svcCfg.Name, svc); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
if err := ln.Init(metadata.MapMetadata(svc.Listener.Metadata)); err != nil {
|
||||
listenerLogger.Fatal("init: ", err)
|
||||
}
|
||||
|
||||
handlerLogger := serviceLogger.WithFields(map[string]interface{}{
|
||||
"kind": "handler",
|
||||
})
|
||||
|
||||
tlsConfig = nil
|
||||
tlsCfg = svc.Handler.TLS
|
||||
if tlsCfg == nil {
|
||||
tlsCfg = &config.TLSConfig{}
|
||||
}
|
||||
tlsConfig, err = loadServerTLSConfig(tlsCfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
h := registry.GetHandler(svc.Handler.Type)(
|
||||
handler.AuthsOption(authsFromConfig(svc.Handler.Auths...)...),
|
||||
handler.RetriesOption(svc.Handler.Retries),
|
||||
handler.ChainOption(chains[svc.Handler.Chain]),
|
||||
handler.BypassOption(bypasses[svc.Bypass]),
|
||||
handler.ResolverOption(resolvers[svc.Resolver]),
|
||||
handler.HostsOption(hosts[svc.Hosts]),
|
||||
handler.TLSConfigOption(tlsConfig),
|
||||
handler.LoggerOption(handlerLogger),
|
||||
)
|
||||
|
||||
if forwarder, ok := h.(handler.Forwarder); ok {
|
||||
forwarder.Forward(forwarderFromConfig(svc.Forwarder))
|
||||
}
|
||||
|
||||
if svc.Handler.Metadata == nil {
|
||||
svc.Handler.Metadata = make(map[string]interface{})
|
||||
}
|
||||
if err := h.Init(metadata.MapMetadata(svc.Handler.Metadata)); err != nil {
|
||||
handlerLogger.Fatal("init: ", err)
|
||||
}
|
||||
|
||||
s := (&service.Service{}).
|
||||
WithListener(ln).
|
||||
WithHandler(h).
|
||||
WithLogger(serviceLogger)
|
||||
services = append(services, s)
|
||||
|
||||
serviceLogger.Infof("listening on %s/%s", s.Addr().String(), s.Addr().Network())
|
||||
services = append(services, svc)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func chainFromConfig(cfg *config.ChainConfig) *chain.Chain {
|
||||
if cfg == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
chainLogger := log.WithFields(map[string]interface{}{
|
||||
"kind": "chain",
|
||||
"chain": cfg.Name,
|
||||
})
|
||||
|
||||
c := &chain.Chain{}
|
||||
selector := selectorFromConfig(cfg.Selector)
|
||||
for _, hop := range cfg.Hops {
|
||||
group := &chain.NodeGroup{}
|
||||
for _, v := range hop.Nodes {
|
||||
nodeLogger := chainLogger.WithFields(map[string]interface{}{
|
||||
"kind": "node",
|
||||
"connector": v.Connector.Type,
|
||||
"dialer": v.Dialer.Type,
|
||||
"hop": hop.Name,
|
||||
"node": v.Name,
|
||||
})
|
||||
connectorLogger := nodeLogger.WithFields(map[string]interface{}{
|
||||
"kind": "connector",
|
||||
})
|
||||
|
||||
var user *url.Userinfo
|
||||
if auth := v.Connector.Auth; auth != nil && auth.Username != "" {
|
||||
if auth.Password == "" {
|
||||
user = url.User(auth.Username)
|
||||
} else {
|
||||
user = url.UserPassword(auth.Username, auth.Password)
|
||||
}
|
||||
}
|
||||
|
||||
var tlsConfig *tls.Config
|
||||
var err error
|
||||
tlsCfg := v.Connector.TLS
|
||||
if tlsCfg == nil {
|
||||
tlsCfg = &config.TLSConfig{}
|
||||
}
|
||||
tlsConfig, err = loadClientTLSConfig(tlsCfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
cr := registry.GetConnector(v.Connector.Type)(
|
||||
connector.UserOption(user),
|
||||
connector.TLSConfigOption(tlsConfig),
|
||||
connector.LoggerOption(connectorLogger),
|
||||
)
|
||||
|
||||
if v.Connector.Metadata == nil {
|
||||
v.Connector.Metadata = make(map[string]interface{})
|
||||
}
|
||||
if err := cr.Init(metadata.MapMetadata(v.Connector.Metadata)); err != nil {
|
||||
connectorLogger.Fatal("init: ", err)
|
||||
}
|
||||
|
||||
dialerLogger := nodeLogger.WithFields(map[string]interface{}{
|
||||
"kind": "dialer",
|
||||
})
|
||||
|
||||
user = nil
|
||||
if auth := v.Dialer.Auth; auth != nil && auth.Username != "" {
|
||||
if auth.Password == "" {
|
||||
user = url.User(auth.Username)
|
||||
} else {
|
||||
user = url.UserPassword(auth.Username, auth.Password)
|
||||
}
|
||||
}
|
||||
|
||||
tlsConfig = nil
|
||||
tlsCfg = v.Dialer.TLS
|
||||
if tlsCfg == nil {
|
||||
tlsCfg = &config.TLSConfig{}
|
||||
}
|
||||
tlsConfig, err = loadClientTLSConfig(tlsCfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
d := registry.GetDialer(v.Dialer.Type)(
|
||||
dialer.UserOption(user),
|
||||
dialer.TLSConfigOption(tlsConfig),
|
||||
dialer.LoggerOption(dialerLogger),
|
||||
)
|
||||
|
||||
if v.Dialer.Metadata == nil {
|
||||
v.Dialer.Metadata = make(map[string]interface{})
|
||||
}
|
||||
if err := d.Init(metadata.MapMetadata(v.Dialer.Metadata)); err != nil {
|
||||
dialerLogger.Fatal("init: ", err)
|
||||
}
|
||||
|
||||
tr := (&chain.Transport{}).
|
||||
WithConnector(cr).
|
||||
WithDialer(d).
|
||||
WithAddr(v.Addr)
|
||||
|
||||
if v.Bypass == "" {
|
||||
v.Bypass = hop.Bypass
|
||||
}
|
||||
if v.Resolver == "" {
|
||||
v.Resolver = hop.Resolver
|
||||
}
|
||||
if v.Hosts == "" {
|
||||
v.Hosts = hop.Hosts
|
||||
}
|
||||
|
||||
node := &chain.Node{
|
||||
Name: v.Name,
|
||||
Addr: v.Addr,
|
||||
Transport: tr,
|
||||
Bypass: bypasses[v.Bypass],
|
||||
Resolver: resolvers[v.Resolver],
|
||||
Hosts: hosts[v.Hosts],
|
||||
Marker: &chain.FailMarker{},
|
||||
}
|
||||
group.AddNode(node)
|
||||
}
|
||||
|
||||
sel := selector
|
||||
if s := selectorFromConfig(hop.Selector); s != nil {
|
||||
sel = s
|
||||
}
|
||||
group.WithSelector(sel)
|
||||
c.AddNodeGroup(group)
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func forwarderFromConfig(cfg *config.ForwarderConfig) *chain.NodeGroup {
|
||||
if cfg == nil || len(cfg.Targets) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
group := &chain.NodeGroup{}
|
||||
for _, target := range cfg.Targets {
|
||||
if v := strings.TrimSpace(target); v != "" {
|
||||
group.AddNode(&chain.Node{
|
||||
Name: target,
|
||||
Addr: target,
|
||||
Marker: &chain.FailMarker{},
|
||||
})
|
||||
}
|
||||
}
|
||||
return group.WithSelector(selectorFromConfig(cfg.Selector))
|
||||
}
|
||||
|
||||
func logFromConfig(cfg *config.LogConfig) logger.Logger {
|
||||
if cfg == nil {
|
||||
cfg = &config.LogConfig{}
|
||||
@@ -314,7 +100,7 @@ func logFromConfig(cfg *config.LogConfig) logger.Logger {
|
||||
|
||||
var out io.Writer = os.Stderr
|
||||
switch cfg.Output {
|
||||
case "none":
|
||||
case "none", "null":
|
||||
return logger.Nop()
|
||||
case "stdout":
|
||||
out = os.Stdout
|
||||
@@ -333,104 +119,15 @@ func logFromConfig(cfg *config.LogConfig) logger.Logger {
|
||||
return logger.NewLogger(opts...)
|
||||
}
|
||||
|
||||
func selectorFromConfig(cfg *config.SelectorConfig) chain.Selector {
|
||||
if cfg == nil {
|
||||
return nil
|
||||
func buildAPIServer(cfg *config.APIConfig) (*api.Server, error) {
|
||||
auther := parsing.ParseAutherFromAuth(cfg.Auth)
|
||||
if cfg.Auther != "" {
|
||||
auther = registry.Auther().Get(cfg.Auther)
|
||||
}
|
||||
|
||||
var strategy chain.Strategy
|
||||
switch cfg.Strategy {
|
||||
case "round", "rr":
|
||||
strategy = chain.RoundRobinStrategy()
|
||||
case "random", "rand":
|
||||
strategy = chain.RandomStrategy()
|
||||
case "fifo", "ha":
|
||||
strategy = chain.FIFOStrategy()
|
||||
default:
|
||||
strategy = chain.RoundRobinStrategy()
|
||||
}
|
||||
|
||||
return chain.NewSelector(
|
||||
strategy,
|
||||
chain.InvalidFilter(),
|
||||
chain.FailFilter(cfg.MaxFails, cfg.FailTimeout),
|
||||
return api.NewServer(
|
||||
cfg.Addr,
|
||||
api.PathPrefixOption(cfg.PathPrefix),
|
||||
api.AccessLogOption(cfg.AccessLog),
|
||||
api.AutherOption(auther),
|
||||
)
|
||||
}
|
||||
|
||||
func bypassFromConfig(cfg *config.BypassConfig) bypass.Bypass {
|
||||
if cfg == nil {
|
||||
return nil
|
||||
}
|
||||
return bypass.NewBypassPatterns(
|
||||
cfg.Reverse,
|
||||
cfg.Matchers,
|
||||
bypass.LoggerBypassOption(log.WithFields(map[string]interface{}{
|
||||
"kind": "bypass",
|
||||
"bypass": cfg.Name,
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
func resolverFromConfig(cfg *config.ResolverConfig) (resolver.Resolver, error) {
|
||||
if cfg == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var nameservers []resolver_impl.NameServer
|
||||
for _, server := range cfg.Nameservers {
|
||||
nameservers = append(nameservers, resolver_impl.NameServer{
|
||||
Addr: server.Addr,
|
||||
Chain: chains[server.Chain],
|
||||
TTL: server.TTL,
|
||||
Timeout: server.Timeout,
|
||||
ClientIP: net.ParseIP(server.ClientIP),
|
||||
Prefer: server.Prefer,
|
||||
Hostname: server.Hostname,
|
||||
})
|
||||
}
|
||||
|
||||
logger := log.WithFields(map[string]interface{}{
|
||||
"kind": "resolver",
|
||||
"resolver": cfg.Name,
|
||||
})
|
||||
return resolver_impl.NewResolver(
|
||||
nameservers,
|
||||
resolver_impl.LoggerResolverOption(logger),
|
||||
)
|
||||
}
|
||||
|
||||
func hostsFromConfig(cfg *config.HostsConfig) hostspkg.HostMapper {
|
||||
if cfg == nil || len(cfg.Mappings) == 0 {
|
||||
return nil
|
||||
}
|
||||
hosts := hostspkg.NewHosts()
|
||||
hosts.Logger = log.WithFields(map[string]interface{}{
|
||||
"kind": "hosts",
|
||||
"hosts": cfg.Name,
|
||||
})
|
||||
|
||||
for _, host := range cfg.Mappings {
|
||||
if host.IP == "" || host.Hostname == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
ip := net.ParseIP(host.IP)
|
||||
if ip == nil {
|
||||
continue
|
||||
}
|
||||
hosts.Map(ip, host.Hostname, host.Aliases...)
|
||||
}
|
||||
return hosts
|
||||
}
|
||||
|
||||
func authsFromConfig(cfgs ...*config.AuthConfig) []*url.Userinfo {
|
||||
var auths []*url.Userinfo
|
||||
|
||||
for _, cfg := range cfgs {
|
||||
if cfg == nil || cfg.Username == "" {
|
||||
continue
|
||||
}
|
||||
auths = append(auths, url.UserPassword(cfg.Username, cfg.Password))
|
||||
}
|
||||
|
||||
return auths
|
||||
}
|
||||
|
||||
+36
-24
@@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
@@ -16,11 +15,12 @@ import (
|
||||
var (
|
||||
log = logger.Default()
|
||||
|
||||
cfgFile string
|
||||
outputCfgFile string
|
||||
services stringList
|
||||
nodes stringList
|
||||
debug bool
|
||||
cfgFile string
|
||||
outputFormat string
|
||||
services stringList
|
||||
nodes stringList
|
||||
debug bool
|
||||
apiAddr string
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -30,8 +30,9 @@ func init() {
|
||||
flag.Var(&nodes, "F", "chain node list")
|
||||
flag.StringVar(&cfgFile, "C", "", "configure file")
|
||||
flag.BoolVar(&printVersion, "V", false, "print version")
|
||||
flag.StringVar(&outputFormat, "O", "", "output format, one of yaml|json format")
|
||||
flag.BoolVar(&debug, "D", false, "debug mode")
|
||||
flag.StringVar(&outputCfgFile, "O", "", "write config to FILE")
|
||||
flag.StringVar(&apiAddr, "api", "", "api server addr")
|
||||
flag.Parse()
|
||||
|
||||
if printVersion {
|
||||
@@ -44,7 +45,7 @@ func init() {
|
||||
func main() {
|
||||
cfg := &config.Config{}
|
||||
var err error
|
||||
if len(services) > 0 {
|
||||
if len(services) > 0 || apiAddr != "" {
|
||||
cfg, err = buildConfigFromCmd(services, nodes)
|
||||
if debug && cfg != nil {
|
||||
if cfg.Log == nil {
|
||||
@@ -52,6 +53,11 @@ func main() {
|
||||
}
|
||||
cfg.Log.Level = string(logger.DebugLevel)
|
||||
}
|
||||
if apiAddr != "" {
|
||||
cfg.API = &config.APIConfig{
|
||||
Addr: apiAddr,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if cfgFile != "" {
|
||||
err = cfg.ReadFile(cfgFile)
|
||||
@@ -65,41 +71,47 @@ func main() {
|
||||
|
||||
log = logFromConfig(cfg.Log)
|
||||
|
||||
if outputCfgFile != "" {
|
||||
var w io.Writer
|
||||
if outputCfgFile == "-" {
|
||||
w = os.Stdout
|
||||
} else {
|
||||
f, err := os.Create(outputCfgFile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
w = f
|
||||
}
|
||||
if err := cfg.Write(w); err != nil {
|
||||
logger.SetDefault(log)
|
||||
|
||||
if outputFormat != "" {
|
||||
if err := cfg.Write(os.Stdout, outputFormat); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if cfg.Profiling != nil && cfg.Profiling.Enabled {
|
||||
if cfg.Profiling != nil && cfg.Profiling.Enable {
|
||||
go func() {
|
||||
addr := cfg.Profiling.Addr
|
||||
if addr == "" {
|
||||
addr = ":6060"
|
||||
}
|
||||
log.Info("profiling serve on ", addr)
|
||||
log.Info("profiling server on ", addr)
|
||||
log.Fatal(http.ListenAndServe(addr, nil))
|
||||
}()
|
||||
}
|
||||
|
||||
if cfg.API != nil {
|
||||
s, err := buildAPIServer(cfg.API)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
go func() {
|
||||
log.Info("api server on ", s.Addr())
|
||||
log.Fatal(s.Serve())
|
||||
}()
|
||||
}
|
||||
|
||||
buildDefaultTLSConfig(cfg.TLS)
|
||||
|
||||
services := buildService(cfg)
|
||||
for _, svc := range services {
|
||||
go svc.Run()
|
||||
go svc.Serve()
|
||||
}
|
||||
|
||||
config.SetGlobal(cfg)
|
||||
|
||||
select {}
|
||||
}
|
||||
|
||||
+12
-11
@@ -14,14 +14,6 @@ import (
|
||||
"github.com/go-gost/gost/pkg/config"
|
||||
)
|
||||
|
||||
func loadServerTLSConfig(cfg *config.TLSConfig) (*tls.Config, error) {
|
||||
return tls_util.LoadServerConfig(cfg.CertFile, cfg.KeyFile, cfg.CAFile)
|
||||
}
|
||||
|
||||
func loadClientTLSConfig(cfg *config.TLSConfig) (*tls.Config, error) {
|
||||
return tls_util.LoadClientConfig(cfg.CertFile, cfg.KeyFile, cfg.CAFile, cfg.Secure, cfg.ServerName)
|
||||
}
|
||||
|
||||
func buildDefaultTLSConfig(cfg *config.TLSConfig) {
|
||||
if cfg == nil {
|
||||
cfg = &config.TLSConfig{
|
||||
@@ -80,12 +72,15 @@ func generateKeyPair() (rawCert, rawKey []byte, err error) {
|
||||
notBefore := time.Now()
|
||||
notAfter := notBefore.Add(validFor)
|
||||
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
|
||||
serialNumber, _ := rand.Int(rand.Reader, serialNumberLimit)
|
||||
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: serialNumber,
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"gost"},
|
||||
CommonName: "gost.run",
|
||||
},
|
||||
NotBefore: notBefore,
|
||||
NotAfter: notAfter,
|
||||
@@ -94,13 +89,19 @@ func generateKeyPair() (rawCert, rawKey []byte, err error) {
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
template.DNSNames = append(template.DNSNames, "gost.run")
|
||||
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
rawCert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
|
||||
rawKey = pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
|
||||
privBytes, err := x509.MarshalPKCS8PrivateKey(priv)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
rawKey = pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privBytes})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
package main
|
||||
|
||||
const (
|
||||
version = "3.0.0-alpha.1"
|
||||
version = "3.0.0-alpha.2"
|
||||
)
|
||||
|
||||
@@ -10,6 +10,8 @@ require (
|
||||
github.com/coreos/go-iptables v0.5.0 // indirect
|
||||
github.com/docker/libcontainer v2.2.1+incompatible
|
||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||
github.com/gin-contrib/cors v1.3.1
|
||||
github.com/gin-gonic/gin v1.7.7
|
||||
github.com/go-gost/gosocks4 v0.0.1
|
||||
github.com/go-gost/gosocks5 v0.3.1-0.20211109033403-d894d75b7f09
|
||||
github.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7
|
||||
@@ -60,14 +62,24 @@ require (
|
||||
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/grpc v1.44.0
|
||||
google.golang.org/protobuf v1.27.1
|
||||
gopkg.in/ini.v1 v1.63.2 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.13.0 // indirect
|
||||
github.com/go-playground/universal-translator v0.17.0 // indirect
|
||||
github.com/go-playground/validator/v10 v10.4.1 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/json-iterator/go v1.1.11 // indirect
|
||||
github.com/leodido/go-urn v1.2.0 // indirect
|
||||
github.com/marten-seemann/qpack v0.2.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.12 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||
github.com/ugorji/go/codec v1.1.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
)
|
||||
|
||||
@@ -114,6 +114,13 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4
|
||||
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
|
||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/cors v1.3.1 h1:doAsuITavI4IOcd0Y19U4B+O0dNWihRyX//nn4sEmgA=
|
||||
github.com/gin-contrib/cors v1.3.1/go.mod h1:jjEJ4268OPZUcU7k9Pm653S7lXUGcqMADzFA61xsmDk=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
|
||||
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
|
||||
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
|
||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
@@ -127,6 +134,16 @@ github.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7 h1:itaaJhQJ19kUXEB
|
||||
github.com/go-gost/relay v0.1.1-0.20211123134818-8ef7fd81ffd7/go.mod h1:lcX+23LCQ3khIeASBo+tJ/WbwXFO32/N5YN6ucuYTG8=
|
||||
github.com/go-gost/tls-dissector v0.0.2-0.20211125135007-2b5d5bd9c07e h1:73NGqAs22ey3wJkIYVD/ACEoovuIuOlEzQTEoqrO5+U=
|
||||
github.com/go-gost/tls-dissector v0.0.2-0.20211125135007-2b5d5bd9c07e/go.mod h1:/9QfdewqmHdaE362Hv5nDaSWLx3pCmtD870d6GaquXs=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
|
||||
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
|
||||
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
|
||||
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
@@ -247,6 +264,9 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
@@ -265,6 +285,9 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
||||
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/lucas-clemente/quic-go v0.24.0 h1:ToR7SIIEdrgOhgVTHvPgdVRJfgVy+N0wQAagH7L4d5g=
|
||||
github.com/lucas-clemente/quic-go v0.24.0/go.mod h1:paZuzjXCE5mj6sikVLMvqXk8lJV2AsqtJ6bDhjEfxx0=
|
||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
@@ -283,8 +306,10 @@ github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
||||
@@ -304,8 +329,10 @@ github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
|
||||
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 h1:ULR/QWMgcgRiZLUjSSJMU+fW+RDMstRdmnDWj9Q+AsA=
|
||||
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104/go.mod h1:wqKykBG2QzQDJEzvRkcS8x6MiSJkF52hXZsXcjaB3ls=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
@@ -411,6 +438,10 @@ github.com/templexxx/xorsimd v0.4.1 h1:iUZcywbOYDRAZUasAs2eSCUW8eobuZDy0I9FJiORk
|
||||
github.com/templexxx/xorsimd v0.4.1/go.mod h1:W+ffZz8jJMH2SXwuKu9WhygqBMbFnp14G2fqEr8qaNo=
|
||||
github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM=
|
||||
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
|
||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
||||
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
||||
github.com/xtaci/kcp-go/v5 v5.6.1 h1:Pwn0aoeNSPF9dTS7IgiPXn0HEtaIlVb6y5UKWPsx8bI=
|
||||
@@ -543,8 +574,6 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT
|
||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY=
|
||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -592,6 +621,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -640,14 +670,14 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf h1:2ucpDCmfkl8Bd/FsLtiD653Wf96cW37s+iGx93zsu4k=
|
||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -655,7 +685,6 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
@@ -823,7 +852,6 @@ google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKr
|
||||
google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
|
||||
google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
|
||||
google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71 h1:z+ErRPu0+KS02Td3fOAgdX+lnPDh/VyaABEJPD4JRQs=
|
||||
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
|
||||
google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350 h1:YxHp5zqIcAShDEvRr5/0rVESVS+njYF68PSdazrNLJo=
|
||||
google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
@@ -854,7 +882,6 @@ google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.44.0 h1:weqSxi/TMs1SqFRMHCtBgXRs8k3X39QIDEZ0pRcttUg=
|
||||
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
@@ -879,6 +906,8 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.63.2 h1:tGK/CyBg7SMzb60vP1M03vNZ3VDu3wGQJwn7Sxi9r3c=
|
||||
gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package admission
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-gost/gost/pkg/common/matcher"
|
||||
"github.com/go-gost/gost/pkg/logger"
|
||||
)
|
||||
|
||||
type Admission interface {
|
||||
Admit(addr string) bool
|
||||
}
|
||||
|
||||
type options struct {
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
type Option func(opts *options)
|
||||
|
||||
func LoggerOption(logger logger.Logger) Option {
|
||||
return func(opts *options) {
|
||||
opts.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
type admission struct {
|
||||
matchers []matcher.Matcher
|
||||
reversed bool
|
||||
options options
|
||||
}
|
||||
|
||||
// NewAdmission creates and initializes a new Admission using matchers as its match rules.
|
||||
// The rules will be reversed if the reversed is true.
|
||||
func NewAdmission(reversed bool, matchers []matcher.Matcher, opts ...Option) Admission {
|
||||
options := options{}
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
return &admission{
|
||||
matchers: matchers,
|
||||
reversed: reversed,
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
// NewAdmissionPatterns creates and initializes a new Admission using matcher patterns as its match rules.
|
||||
// The rules will be reversed if the reverse is true.
|
||||
func NewAdmissionPatterns(reversed bool, patterns []string, opts ...Option) Admission {
|
||||
var matchers []matcher.Matcher
|
||||
for _, pattern := range patterns {
|
||||
if m := matcher.NewMatcher(pattern); m != nil {
|
||||
matchers = append(matchers, m)
|
||||
}
|
||||
}
|
||||
return NewAdmission(reversed, matchers, opts...)
|
||||
}
|
||||
|
||||
func (p *admission) Admit(addr string) bool {
|
||||
if addr == "" || p == nil || len(p.matchers) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// try to strip the port
|
||||
if host, port, _ := net.SplitHostPort(addr); host != "" && port != "" {
|
||||
if p, _ := strconv.Atoi(port); p > 0 { // port is valid
|
||||
addr = host
|
||||
}
|
||||
}
|
||||
|
||||
var matched bool
|
||||
for _, matcher := range p.matchers {
|
||||
if matcher == nil {
|
||||
continue
|
||||
}
|
||||
if matcher.Match(addr) {
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
b := !p.reversed && matched ||
|
||||
p.reversed && !matched
|
||||
return b
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"embed"
|
||||
)
|
||||
|
||||
var (
|
||||
//go:embed swagger.yaml
|
||||
swaggerDoc embed.FS
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
Code int `json:"code,omitempty"`
|
||||
Msg string `json:"msg,omitempty"`
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-gost/gost/pkg/config"
|
||||
)
|
||||
|
||||
// swagger:parameters getConfigRequest
|
||||
type getConfigRequest struct {
|
||||
// output format, one of yaml|json, default is json.
|
||||
// in: query
|
||||
Format string `form:"format" json:"format"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response getConfigResponse
|
||||
type getConfigResponse struct {
|
||||
Config *config.Config
|
||||
}
|
||||
|
||||
func getConfig(ctx *gin.Context) {
|
||||
// swagger:route GET /config ConfigManagement getConfigRequest
|
||||
//
|
||||
// Get current config.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: getConfigResponse
|
||||
|
||||
var req getConfigRequest
|
||||
ctx.ShouldBindQuery(&req)
|
||||
|
||||
var resp getConfigResponse
|
||||
resp.Config = config.Global()
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
switch req.Format {
|
||||
case "yaml":
|
||||
default:
|
||||
req.Format = "json"
|
||||
}
|
||||
|
||||
resp.Config.Write(buf, req.Format)
|
||||
|
||||
contentType := "application/json"
|
||||
if req.Format == "yaml" {
|
||||
contentType = "text/x-yaml"
|
||||
}
|
||||
|
||||
ctx.Data(http.StatusOK, contentType, buf.Bytes())
|
||||
}
|
||||
|
||||
// swagger:parameters saveConfigRequest
|
||||
type saveConfigRequest struct {
|
||||
// output format, one of yaml|json, default is yaml.
|
||||
// in: query
|
||||
Format string `form:"format" json:"format"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response saveConfigResponse
|
||||
type saveConfigResponse struct {
|
||||
Data Response
|
||||
}
|
||||
|
||||
func saveConfig(ctx *gin.Context) {
|
||||
// swagger:route POST /config ConfigManagement saveConfigRequest
|
||||
//
|
||||
// Save current config to file (gost.yaml or gost.json).
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: saveConfigResponse
|
||||
|
||||
var req saveConfigRequest
|
||||
ctx.ShouldBindQuery(&req)
|
||||
|
||||
file := "gost.yaml"
|
||||
switch req.Format {
|
||||
case "json":
|
||||
file = "gost.json"
|
||||
default:
|
||||
req.Format = "yaml"
|
||||
}
|
||||
|
||||
f, err := os.Create(file)
|
||||
if err != nil {
|
||||
writeError(ctx, &Error{
|
||||
statusCode: http.StatusInternalServerError,
|
||||
Code: 40005,
|
||||
Msg: fmt.Sprintf("create file: %s", err.Error()),
|
||||
})
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
if err := config.Global().Write(f, req.Format); err != nil {
|
||||
writeError(ctx, &Error{
|
||||
statusCode: http.StatusInternalServerError,
|
||||
Code: 40006,
|
||||
Msg: fmt.Sprintf("write: %s", err.Error()),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Msg: "OK",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-gost/gost/pkg/config"
|
||||
"github.com/go-gost/gost/pkg/config/parsing"
|
||||
"github.com/go-gost/gost/pkg/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters createAdmissionRequest
|
||||
type createAdmissionRequest struct {
|
||||
// in: body
|
||||
Data config.AdmissionConfig `json:"data"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response createAdmissionResponse
|
||||
type createAdmissionResponse struct {
|
||||
Data Response
|
||||
}
|
||||
|
||||
func createAdmission(ctx *gin.Context) {
|
||||
// swagger:route POST /config/admissions ConfigManagement createAdmissionRequest
|
||||
//
|
||||
// Create a new admission, the name of admission must be unique in admission list.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: createAdmissionResponse
|
||||
|
||||
var req createAdmissionRequest
|
||||
ctx.ShouldBindJSON(&req.Data)
|
||||
|
||||
if req.Data.Name == "" {
|
||||
writeError(ctx, ErrInvalid)
|
||||
return
|
||||
}
|
||||
|
||||
v := parsing.ParseAdmission(&req.Data)
|
||||
|
||||
if err := registry.Admission().Register(req.Data.Name, v); err != nil {
|
||||
writeError(ctx, ErrDup)
|
||||
return
|
||||
}
|
||||
|
||||
cfg := config.Global()
|
||||
cfg.Admissions = append(cfg.Admissions, &req.Data)
|
||||
config.SetGlobal(cfg)
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Msg: "OK",
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters updateAdmissionRequest
|
||||
type updateAdmissionRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Admission string `uri:"admission" json:"admission"`
|
||||
// in: body
|
||||
Data config.AdmissionConfig `json:"data"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response updateAdmissionResponse
|
||||
type updateAdmissionResponse struct {
|
||||
Data Response
|
||||
}
|
||||
|
||||
func updateAdmission(ctx *gin.Context) {
|
||||
// swagger:route PUT /config/admissions/{admission} ConfigManagement updateAdmissionRequest
|
||||
//
|
||||
// Update admission by name, the admission must already exist.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: updateAdmissionResponse
|
||||
|
||||
var req updateAdmissionRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
ctx.ShouldBindJSON(&req.Data)
|
||||
|
||||
if !registry.Admission().IsRegistered(req.Admission) {
|
||||
writeError(ctx, ErrNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
req.Data.Name = req.Admission
|
||||
|
||||
v := parsing.ParseAdmission(&req.Data)
|
||||
|
||||
registry.Admission().Unregister(req.Admission)
|
||||
|
||||
if err := registry.Admission().Register(req.Admission, v); err != nil {
|
||||
writeError(ctx, ErrDup)
|
||||
return
|
||||
}
|
||||
|
||||
cfg := config.Global()
|
||||
for i := range cfg.Admissions {
|
||||
if cfg.Admissions[i].Name == req.Admission {
|
||||
cfg.Admissions[i] = &req.Data
|
||||
break
|
||||
}
|
||||
}
|
||||
config.SetGlobal(cfg)
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Msg: "OK",
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters deleteAdmissionRequest
|
||||
type deleteAdmissionRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Admission string `uri:"admission" json:"admission"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response deleteAdmissionResponse
|
||||
type deleteAdmissionResponse struct {
|
||||
Data Response
|
||||
}
|
||||
|
||||
func deleteAdmission(ctx *gin.Context) {
|
||||
// swagger:route DELETE /config/admissions/{admission} ConfigManagement deleteAdmissionRequest
|
||||
//
|
||||
// Delete admission by name.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: deleteAdmissionResponse
|
||||
|
||||
var req deleteAdmissionRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
|
||||
if !registry.Admission().IsRegistered(req.Admission) {
|
||||
writeError(ctx, ErrNotFound)
|
||||
return
|
||||
}
|
||||
registry.Admission().Unregister(req.Admission)
|
||||
|
||||
cfg := config.Global()
|
||||
admissiones := cfg.Admissions
|
||||
cfg.Admissions = nil
|
||||
for _, s := range admissiones {
|
||||
if s.Name == req.Admission {
|
||||
continue
|
||||
}
|
||||
cfg.Admissions = append(cfg.Admissions, s)
|
||||
}
|
||||
config.SetGlobal(cfg)
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Msg: "OK",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-gost/gost/pkg/config"
|
||||
"github.com/go-gost/gost/pkg/config/parsing"
|
||||
"github.com/go-gost/gost/pkg/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters createAutherRequest
|
||||
type createAutherRequest struct {
|
||||
// in: body
|
||||
Data config.AutherConfig `json:"data"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response createAutherResponse
|
||||
type createAutherResponse struct {
|
||||
Data Response
|
||||
}
|
||||
|
||||
func createAuther(ctx *gin.Context) {
|
||||
// swagger:route POST /config/authers ConfigManagement createAutherRequest
|
||||
//
|
||||
// Create a new auther, the name of the auther must be unique in auther list.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: createAutherResponse
|
||||
|
||||
var req createAutherRequest
|
||||
ctx.ShouldBindJSON(&req.Data)
|
||||
|
||||
if req.Data.Name == "" {
|
||||
writeError(ctx, ErrInvalid)
|
||||
return
|
||||
}
|
||||
|
||||
v := parsing.ParseAuther(&req.Data)
|
||||
if err := registry.Auther().Register(req.Data.Name, v); err != nil {
|
||||
writeError(ctx, ErrDup)
|
||||
return
|
||||
}
|
||||
|
||||
cfg := config.Global()
|
||||
cfg.Authers = append(cfg.Authers, &req.Data)
|
||||
config.SetGlobal(cfg)
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Msg: "OK",
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters updateAutherRequest
|
||||
type updateAutherRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Auther string `uri:"auther" json:"auther"`
|
||||
// in: body
|
||||
Data config.AutherConfig `json:"data"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response updateAutherResponse
|
||||
type updateAutherResponse struct {
|
||||
Data Response
|
||||
}
|
||||
|
||||
func updateAuther(ctx *gin.Context) {
|
||||
// swagger:route PUT /config/authers/{auther} ConfigManagement updateAutherRequest
|
||||
//
|
||||
// Update auther by name, the auther must already exist.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: updateAutherResponse
|
||||
|
||||
var req updateAutherRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
ctx.ShouldBindJSON(&req.Data)
|
||||
|
||||
if !registry.Auther().IsRegistered(req.Auther) {
|
||||
writeError(ctx, ErrNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
req.Data.Name = req.Auther
|
||||
|
||||
v := parsing.ParseAuther(&req.Data)
|
||||
registry.Auther().Unregister(req.Auther)
|
||||
|
||||
if err := registry.Auther().Register(req.Auther, v); err != nil {
|
||||
writeError(ctx, ErrDup)
|
||||
return
|
||||
}
|
||||
|
||||
cfg := config.Global()
|
||||
for i := range cfg.Authers {
|
||||
if cfg.Authers[i].Name == req.Auther {
|
||||
cfg.Authers[i] = &req.Data
|
||||
break
|
||||
}
|
||||
}
|
||||
config.SetGlobal(cfg)
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Msg: "OK",
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters deleteAutherRequest
|
||||
type deleteAutherRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Auther string `uri:"auther" json:"auther"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response deleteAutherResponse
|
||||
type deleteAutherResponse struct {
|
||||
Data Response
|
||||
}
|
||||
|
||||
func deleteAuther(ctx *gin.Context) {
|
||||
// swagger:route DELETE /config/authers/{auther} ConfigManagement deleteAutherRequest
|
||||
//
|
||||
// Delete auther by name.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: deleteAutherResponse
|
||||
|
||||
var req deleteAutherRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
|
||||
if !registry.Auther().IsRegistered(req.Auther) {
|
||||
writeError(ctx, ErrNotFound)
|
||||
return
|
||||
}
|
||||
registry.Auther().Unregister(req.Auther)
|
||||
|
||||
cfg := config.Global()
|
||||
authers := cfg.Authers
|
||||
cfg.Authers = nil
|
||||
for _, s := range authers {
|
||||
if s.Name == req.Auther {
|
||||
continue
|
||||
}
|
||||
cfg.Authers = append(cfg.Authers, s)
|
||||
}
|
||||
config.SetGlobal(cfg)
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Msg: "OK",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-gost/gost/pkg/config"
|
||||
"github.com/go-gost/gost/pkg/config/parsing"
|
||||
"github.com/go-gost/gost/pkg/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters createBypassRequest
|
||||
type createBypassRequest struct {
|
||||
// in: body
|
||||
Data config.BypassConfig `json:"data"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response createBypassResponse
|
||||
type createBypassResponse struct {
|
||||
Data Response
|
||||
}
|
||||
|
||||
func createBypass(ctx *gin.Context) {
|
||||
// swagger:route POST /config/bypasses ConfigManagement createBypassRequest
|
||||
//
|
||||
// Create a new bypass, the name of bypass must be unique in bypass list.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: createBypassResponse
|
||||
|
||||
var req createBypassRequest
|
||||
ctx.ShouldBindJSON(&req.Data)
|
||||
|
||||
if req.Data.Name == "" {
|
||||
writeError(ctx, ErrInvalid)
|
||||
return
|
||||
}
|
||||
|
||||
v := parsing.ParseBypass(&req.Data)
|
||||
|
||||
if err := registry.Bypass().Register(req.Data.Name, v); err != nil {
|
||||
writeError(ctx, ErrDup)
|
||||
return
|
||||
}
|
||||
|
||||
cfg := config.Global()
|
||||
cfg.Bypasses = append(cfg.Bypasses, &req.Data)
|
||||
config.SetGlobal(cfg)
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Msg: "OK",
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters updateBypassRequest
|
||||
type updateBypassRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Bypass string `uri:"bypass" json:"bypass"`
|
||||
// in: body
|
||||
Data config.BypassConfig `json:"data"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response updateBypassResponse
|
||||
type updateBypassResponse struct {
|
||||
Data Response
|
||||
}
|
||||
|
||||
func updateBypass(ctx *gin.Context) {
|
||||
// swagger:route PUT /config/bypasses/{bypass} ConfigManagement updateBypassRequest
|
||||
//
|
||||
// Update bypass by name, the bypass must already exist.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: updateBypassResponse
|
||||
|
||||
var req updateBypassRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
ctx.ShouldBindJSON(&req.Data)
|
||||
|
||||
if !registry.Bypass().IsRegistered(req.Bypass) {
|
||||
writeError(ctx, ErrNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
req.Data.Name = req.Bypass
|
||||
|
||||
v := parsing.ParseBypass(&req.Data)
|
||||
|
||||
registry.Bypass().Unregister(req.Bypass)
|
||||
|
||||
if err := registry.Bypass().Register(req.Bypass, v); err != nil {
|
||||
writeError(ctx, ErrDup)
|
||||
return
|
||||
}
|
||||
|
||||
cfg := config.Global()
|
||||
for i := range cfg.Bypasses {
|
||||
if cfg.Bypasses[i].Name == req.Bypass {
|
||||
cfg.Bypasses[i] = &req.Data
|
||||
break
|
||||
}
|
||||
}
|
||||
config.SetGlobal(cfg)
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Msg: "OK",
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters deleteBypassRequest
|
||||
type deleteBypassRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Bypass string `uri:"bypass" json:"bypass"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response deleteBypassResponse
|
||||
type deleteBypassResponse struct {
|
||||
Data Response
|
||||
}
|
||||
|
||||
func deleteBypass(ctx *gin.Context) {
|
||||
// swagger:route DELETE /config/bypasses/{bypass} ConfigManagement deleteBypassRequest
|
||||
//
|
||||
// Delete bypass by name.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: deleteBypassResponse
|
||||
|
||||
var req deleteBypassRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
|
||||
if !registry.Bypass().IsRegistered(req.Bypass) {
|
||||
writeError(ctx, ErrNotFound)
|
||||
return
|
||||
}
|
||||
registry.Bypass().Unregister(req.Bypass)
|
||||
|
||||
cfg := config.Global()
|
||||
bypasses := cfg.Bypasses
|
||||
cfg.Bypasses = nil
|
||||
for _, s := range bypasses {
|
||||
if s.Name == req.Bypass {
|
||||
continue
|
||||
}
|
||||
cfg.Bypasses = append(cfg.Bypasses, s)
|
||||
}
|
||||
config.SetGlobal(cfg)
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Msg: "OK",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-gost/gost/pkg/config"
|
||||
"github.com/go-gost/gost/pkg/config/parsing"
|
||||
"github.com/go-gost/gost/pkg/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters createChainRequest
|
||||
type createChainRequest struct {
|
||||
// in: body
|
||||
Data config.ChainConfig `json:"data"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response createChainResponse
|
||||
type createChainResponse struct {
|
||||
Data Response
|
||||
}
|
||||
|
||||
func createChain(ctx *gin.Context) {
|
||||
// swagger:route POST /config/chains ConfigManagement createChainRequest
|
||||
//
|
||||
// Create a new chain, the name of chain must be unique in chain list.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: createChainResponse
|
||||
|
||||
var req createChainRequest
|
||||
ctx.ShouldBindJSON(&req.Data)
|
||||
|
||||
if req.Data.Name == "" {
|
||||
writeError(ctx, ErrInvalid)
|
||||
return
|
||||
}
|
||||
|
||||
v, err := parsing.ParseChain(&req.Data)
|
||||
if err != nil {
|
||||
writeError(ctx, ErrCreate)
|
||||
return
|
||||
}
|
||||
|
||||
if err := registry.Chain().Register(req.Data.Name, v); err != nil {
|
||||
writeError(ctx, ErrDup)
|
||||
return
|
||||
}
|
||||
|
||||
cfg := config.Global()
|
||||
cfg.Chains = append(cfg.Chains, &req.Data)
|
||||
config.SetGlobal(cfg)
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Msg: "OK",
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters updateChainRequest
|
||||
type updateChainRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
// chain name
|
||||
Chain string `uri:"chain" json:"chain"`
|
||||
// in: body
|
||||
Data config.ChainConfig `json:"data"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response updateChainResponse
|
||||
type updateChainResponse struct {
|
||||
Data Response
|
||||
}
|
||||
|
||||
func updateChain(ctx *gin.Context) {
|
||||
// swagger:route PUT /config/chains/{chain} ConfigManagement updateChainRequest
|
||||
//
|
||||
// Update chain by name, the chain must already exist.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: updateChainResponse
|
||||
|
||||
var req updateChainRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
ctx.ShouldBindJSON(&req.Data)
|
||||
|
||||
if !registry.Chain().IsRegistered(req.Chain) {
|
||||
writeError(ctx, ErrNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
req.Data.Name = req.Chain
|
||||
|
||||
v, err := parsing.ParseChain(&req.Data)
|
||||
if err != nil {
|
||||
writeError(ctx, ErrCreate)
|
||||
return
|
||||
}
|
||||
|
||||
registry.Chain().Unregister(req.Chain)
|
||||
|
||||
if err := registry.Chain().Register(req.Chain, v); err != nil {
|
||||
writeError(ctx, ErrDup)
|
||||
return
|
||||
}
|
||||
|
||||
cfg := config.Global()
|
||||
for i := range cfg.Chains {
|
||||
if cfg.Chains[i].Name == req.Chain {
|
||||
cfg.Chains[i] = &req.Data
|
||||
break
|
||||
}
|
||||
}
|
||||
config.SetGlobal(cfg)
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Msg: "OK",
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters deleteChainRequest
|
||||
type deleteChainRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Chain string `uri:"chain" json:"chain"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response deleteChainResponse
|
||||
type deleteChainResponse struct {
|
||||
Data Response
|
||||
}
|
||||
|
||||
func deleteChain(ctx *gin.Context) {
|
||||
// swagger:route DELETE /config/chains/{chain} ConfigManagement deleteChainRequest
|
||||
//
|
||||
// Delete chain by name.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: deleteChainResponse
|
||||
|
||||
var req deleteChainRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
|
||||
if !registry.Chain().IsRegistered(req.Chain) {
|
||||
writeError(ctx, ErrNotFound)
|
||||
return
|
||||
}
|
||||
registry.Chain().Unregister(req.Chain)
|
||||
|
||||
cfg := config.Global()
|
||||
chains := cfg.Chains
|
||||
cfg.Chains = nil
|
||||
for _, s := range chains {
|
||||
if s.Name == req.Chain {
|
||||
continue
|
||||
}
|
||||
cfg.Chains = append(cfg.Chains, s)
|
||||
}
|
||||
config.SetGlobal(cfg)
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Msg: "OK",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-gost/gost/pkg/config"
|
||||
"github.com/go-gost/gost/pkg/config/parsing"
|
||||
"github.com/go-gost/gost/pkg/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters createHostsRequest
|
||||
type createHostsRequest struct {
|
||||
// in: body
|
||||
Data config.HostsConfig `json:"data"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response createHostsResponse
|
||||
type createHostsesponse struct {
|
||||
Data Response
|
||||
}
|
||||
|
||||
func createHosts(ctx *gin.Context) {
|
||||
// swagger:route POST /config/hosts ConfigManagement createHostsRequest
|
||||
//
|
||||
// Create a new hosts, the name of the hosts must be unique in hosts list.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: createHostsResponse
|
||||
|
||||
var req createHostsRequest
|
||||
ctx.ShouldBindJSON(&req.Data)
|
||||
|
||||
if req.Data.Name == "" {
|
||||
writeError(ctx, ErrInvalid)
|
||||
return
|
||||
}
|
||||
|
||||
v := parsing.ParseHosts(&req.Data)
|
||||
|
||||
if err := registry.Hosts().Register(req.Data.Name, v); err != nil {
|
||||
writeError(ctx, ErrDup)
|
||||
return
|
||||
}
|
||||
|
||||
cfg := config.Global()
|
||||
cfg.Hosts = append(cfg.Hosts, &req.Data)
|
||||
config.SetGlobal(cfg)
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Msg: "OK",
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters updateHostsRequest
|
||||
type updateHostsRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Hosts string `uri:"hosts" json:"hosts"`
|
||||
// in: body
|
||||
Data config.HostsConfig `json:"data"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response updateHostsResponse
|
||||
type updateHostsResponse struct {
|
||||
Data Response
|
||||
}
|
||||
|
||||
func updateHosts(ctx *gin.Context) {
|
||||
// swagger:route PUT /config/hosts/{hosts} ConfigManagement updateHostsRequest
|
||||
//
|
||||
// Update hosts by name, the hosts must already exist.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: updateHostsResponse
|
||||
|
||||
var req updateHostsRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
ctx.ShouldBindJSON(&req.Data)
|
||||
|
||||
if !registry.Hosts().IsRegistered(req.Hosts) {
|
||||
writeError(ctx, ErrNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
req.Data.Name = req.Hosts
|
||||
|
||||
v := parsing.ParseHosts(&req.Data)
|
||||
|
||||
registry.Hosts().Unregister(req.Hosts)
|
||||
|
||||
if err := registry.Hosts().Register(req.Hosts, v); err != nil {
|
||||
writeError(ctx, ErrDup)
|
||||
return
|
||||
}
|
||||
|
||||
cfg := config.Global()
|
||||
for i := range cfg.Hosts {
|
||||
if cfg.Hosts[i].Name == req.Hosts {
|
||||
cfg.Hosts[i] = &req.Data
|
||||
break
|
||||
}
|
||||
}
|
||||
config.SetGlobal(cfg)
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Msg: "OK",
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters deleteHostsRequest
|
||||
type deleteHostsRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Hosts string `uri:"hosts" json:"hosts"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response deleteHostsResponse
|
||||
type deleteHostsResponse struct {
|
||||
Data Response
|
||||
}
|
||||
|
||||
func deleteHosts(ctx *gin.Context) {
|
||||
// swagger:route DELETE /config/hosts/{hosts} ConfigManagement deleteHostsRequest
|
||||
//
|
||||
// Delete hosts by name.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: deleteHostsResponse
|
||||
|
||||
var req deleteHostsRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
|
||||
if !registry.Hosts().IsRegistered(req.Hosts) {
|
||||
writeError(ctx, ErrNotFound)
|
||||
return
|
||||
}
|
||||
registry.Hosts().Unregister(req.Hosts)
|
||||
|
||||
cfg := config.Global()
|
||||
hosts := cfg.Hosts
|
||||
cfg.Hosts = nil
|
||||
for _, s := range hosts {
|
||||
if s.Name == req.Hosts {
|
||||
continue
|
||||
}
|
||||
cfg.Hosts = append(cfg.Hosts, s)
|
||||
}
|
||||
config.SetGlobal(cfg)
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Msg: "OK",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-gost/gost/pkg/config"
|
||||
"github.com/go-gost/gost/pkg/config/parsing"
|
||||
"github.com/go-gost/gost/pkg/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters createResolverRequest
|
||||
type createResolverRequest struct {
|
||||
// in: body
|
||||
Data config.ResolverConfig `json:"data"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response createResolverResponse
|
||||
type createResolverResponse struct {
|
||||
Data Response
|
||||
}
|
||||
|
||||
func createResolver(ctx *gin.Context) {
|
||||
// swagger:route POST /config/resolvers ConfigManagement createResolverRequest
|
||||
//
|
||||
// Create a new resolver, the name of the resolver must be unique in resolver list.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: createResolverResponse
|
||||
|
||||
var req createResolverRequest
|
||||
ctx.ShouldBindJSON(&req.Data)
|
||||
|
||||
if req.Data.Name == "" {
|
||||
writeError(ctx, ErrInvalid)
|
||||
return
|
||||
}
|
||||
|
||||
v, err := parsing.ParseResolver(&req.Data)
|
||||
if err != nil {
|
||||
writeError(ctx, ErrCreate)
|
||||
return
|
||||
}
|
||||
|
||||
if err := registry.Resolver().Register(req.Data.Name, v); err != nil {
|
||||
writeError(ctx, ErrDup)
|
||||
return
|
||||
}
|
||||
|
||||
cfg := config.Global()
|
||||
cfg.Resolvers = append(cfg.Resolvers, &req.Data)
|
||||
config.SetGlobal(cfg)
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Msg: "OK",
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters updateResolverRequest
|
||||
type updateResolverRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Resolver string `uri:"resolver" json:"resolver"`
|
||||
// in: body
|
||||
Data config.ResolverConfig `json:"data"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response updateResolverResponse
|
||||
type updateResolverResponse struct {
|
||||
Data Response
|
||||
}
|
||||
|
||||
func updateResolver(ctx *gin.Context) {
|
||||
// swagger:route PUT /config/resolvers/{resolver} ConfigManagement updateResolverRequest
|
||||
//
|
||||
// Update resolver by name, the resolver must already exist.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: updateResolverResponse
|
||||
|
||||
var req updateResolverRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
ctx.ShouldBindJSON(&req.Data)
|
||||
|
||||
if !registry.Resolver().IsRegistered(req.Resolver) {
|
||||
writeError(ctx, ErrNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
req.Data.Name = req.Resolver
|
||||
|
||||
v, err := parsing.ParseResolver(&req.Data)
|
||||
if err != nil {
|
||||
writeError(ctx, ErrCreate)
|
||||
return
|
||||
}
|
||||
|
||||
registry.Resolver().Unregister(req.Resolver)
|
||||
|
||||
if err := registry.Resolver().Register(req.Resolver, v); err != nil {
|
||||
writeError(ctx, ErrDup)
|
||||
return
|
||||
}
|
||||
|
||||
cfg := config.Global()
|
||||
for i := range cfg.Resolvers {
|
||||
if cfg.Resolvers[i].Name == req.Resolver {
|
||||
cfg.Resolvers[i] = &req.Data
|
||||
break
|
||||
}
|
||||
}
|
||||
config.SetGlobal(cfg)
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Msg: "OK",
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters deleteResolverRequest
|
||||
type deleteResolverRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Resolver string `uri:"resolver" json:"resolver"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response deleteResolverResponse
|
||||
type deleteResolverResponse struct {
|
||||
Data Response
|
||||
}
|
||||
|
||||
func deleteResolver(ctx *gin.Context) {
|
||||
// swagger:route DELETE /config/resolvers/{resolver} ConfigManagement deleteResolverRequest
|
||||
//
|
||||
// Delete resolver by name.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: deleteResolverResponse
|
||||
|
||||
var req deleteResolverRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
|
||||
if !registry.Resolver().IsRegistered(req.Resolver) {
|
||||
writeError(ctx, ErrNotFound)
|
||||
return
|
||||
}
|
||||
registry.Resolver().Unregister(req.Resolver)
|
||||
|
||||
cfg := config.Global()
|
||||
resolvers := cfg.Resolvers
|
||||
cfg.Resolvers = nil
|
||||
for _, s := range resolvers {
|
||||
if s.Name == req.Resolver {
|
||||
continue
|
||||
}
|
||||
cfg.Resolvers = append(cfg.Resolvers, s)
|
||||
}
|
||||
config.SetGlobal(cfg)
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Msg: "OK",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-gost/gost/pkg/config"
|
||||
"github.com/go-gost/gost/pkg/config/parsing"
|
||||
"github.com/go-gost/gost/pkg/registry"
|
||||
)
|
||||
|
||||
// swagger:parameters createServiceRequest
|
||||
type createServiceRequest struct {
|
||||
// in: body
|
||||
Data config.ServiceConfig `json:"data"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response createServiceResponse
|
||||
type createServiceResponse struct {
|
||||
Data Response
|
||||
}
|
||||
|
||||
func createService(ctx *gin.Context) {
|
||||
// swagger:route POST /config/services ConfigManagement createServiceRequest
|
||||
//
|
||||
// Create a new service, the name of the service must be unique in service list.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: createServiceResponse
|
||||
|
||||
var req createServiceRequest
|
||||
ctx.ShouldBindJSON(&req.Data)
|
||||
|
||||
if req.Data.Name == "" {
|
||||
writeError(ctx, ErrInvalid)
|
||||
return
|
||||
}
|
||||
|
||||
if registry.Service().IsRegistered(req.Data.Name) {
|
||||
writeError(ctx, ErrDup)
|
||||
return
|
||||
}
|
||||
|
||||
svc, err := parsing.ParseService(&req.Data)
|
||||
if err != nil {
|
||||
writeError(ctx, ErrCreate)
|
||||
return
|
||||
}
|
||||
|
||||
if err := registry.Service().Register(req.Data.Name, svc); err != nil {
|
||||
svc.Close()
|
||||
writeError(ctx, ErrDup)
|
||||
return
|
||||
}
|
||||
|
||||
go svc.Serve()
|
||||
|
||||
cfg := config.Global()
|
||||
cfg.Services = append(cfg.Services, &req.Data)
|
||||
config.SetGlobal(cfg)
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Msg: "OK",
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters updateServiceRequest
|
||||
type updateServiceRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Service string `uri:"service" json:"service"`
|
||||
// in: body
|
||||
Data config.ServiceConfig `json:"data"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response updateServiceResponse
|
||||
type updateServiceResponse struct {
|
||||
Data Response
|
||||
}
|
||||
|
||||
func updateService(ctx *gin.Context) {
|
||||
// swagger:route PUT /config/services/{service} ConfigManagement updateServiceRequest
|
||||
//
|
||||
// Update service by name, the service must already exist.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: updateServiceResponse
|
||||
|
||||
var req updateServiceRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
ctx.ShouldBindJSON(&req.Data)
|
||||
|
||||
old := registry.Service().Get(req.Service)
|
||||
if old == nil {
|
||||
writeError(ctx, ErrNotFound)
|
||||
return
|
||||
}
|
||||
old.Close()
|
||||
|
||||
req.Data.Name = req.Service
|
||||
|
||||
svc, err := parsing.ParseService(&req.Data)
|
||||
if err != nil {
|
||||
writeError(ctx, ErrCreate)
|
||||
return
|
||||
}
|
||||
|
||||
registry.Service().Unregister(req.Service)
|
||||
|
||||
if err := registry.Service().Register(req.Service, svc); err != nil {
|
||||
svc.Close()
|
||||
writeError(ctx, ErrDup)
|
||||
return
|
||||
}
|
||||
|
||||
go svc.Serve()
|
||||
|
||||
cfg := config.Global()
|
||||
for i := range cfg.Services {
|
||||
if cfg.Services[i].Name == req.Service {
|
||||
cfg.Services[i] = &req.Data
|
||||
break
|
||||
}
|
||||
}
|
||||
config.SetGlobal(cfg)
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Msg: "OK",
|
||||
})
|
||||
}
|
||||
|
||||
// swagger:parameters deleteServiceRequest
|
||||
type deleteServiceRequest struct {
|
||||
// in: path
|
||||
// required: true
|
||||
Service string `uri:"service" json:"service"`
|
||||
}
|
||||
|
||||
// successful operation.
|
||||
// swagger:response deleteServiceResponse
|
||||
type deleteServiceResponse struct {
|
||||
Data Response
|
||||
}
|
||||
|
||||
func deleteService(ctx *gin.Context) {
|
||||
// swagger:route DELETE /config/services/{service} ConfigManagement deleteServiceRequest
|
||||
//
|
||||
// Delete service by name.
|
||||
//
|
||||
// Security:
|
||||
// basicAuth: []
|
||||
//
|
||||
// Responses:
|
||||
// 200: deleteServiceResponse
|
||||
|
||||
var req deleteServiceRequest
|
||||
ctx.ShouldBindUri(&req)
|
||||
|
||||
svc := registry.Service().Get(req.Service)
|
||||
if svc == nil {
|
||||
writeError(ctx, ErrNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
registry.Service().Unregister(req.Service)
|
||||
svc.Close()
|
||||
|
||||
cfg := config.Global()
|
||||
services := cfg.Services
|
||||
cfg.Services = nil
|
||||
for _, s := range services {
|
||||
if s.Name == req.Service {
|
||||
continue
|
||||
}
|
||||
cfg.Services = append(cfg.Services, s)
|
||||
}
|
||||
config.SetGlobal(cfg)
|
||||
|
||||
ctx.JSON(http.StatusOK, Response{
|
||||
Msg: "OK",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// Documentation of Web API.
|
||||
//
|
||||
// Schemes: https, http
|
||||
// BasePath: /
|
||||
// Version: 1.0.0
|
||||
//
|
||||
// Consumes:
|
||||
// - application/json
|
||||
//
|
||||
// Produces:
|
||||
// - application/json
|
||||
//
|
||||
// SecurityDefinitions:
|
||||
// basicAuth:
|
||||
// type: basic
|
||||
//
|
||||
// swagger:meta
|
||||
package api
|
||||
@@ -0,0 +1,45 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalid = &Error{statusCode: http.StatusBadRequest, Code: 40001, Msg: "object invalid"}
|
||||
ErrDup = &Error{statusCode: http.StatusBadRequest, Code: 40002, Msg: "object duplicated"}
|
||||
ErrCreate = &Error{statusCode: http.StatusConflict, Code: 40003, Msg: "object creation failed"}
|
||||
ErrNotFound = &Error{statusCode: http.StatusBadRequest, Code: 40004, Msg: "object not found"}
|
||||
ErrSave = &Error{statusCode: http.StatusInternalServerError, Code: 40005, Msg: "save config failed"}
|
||||
)
|
||||
|
||||
// Error is an api error.
|
||||
type Error struct {
|
||||
statusCode int
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
b, _ := json.Marshal(e)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func writeError(c *gin.Context, err error) {
|
||||
// c.Set(HTTPResponseTag, err)
|
||||
c.JSON(getStatusCode(err), err)
|
||||
}
|
||||
|
||||
func getStatusCode(err error) int {
|
||||
if err == nil {
|
||||
return http.StatusOK
|
||||
}
|
||||
if e, ok := err.(*Error); ok {
|
||||
if e.statusCode >= http.StatusOK && e.statusCode < 600 {
|
||||
return e.statusCode
|
||||
}
|
||||
}
|
||||
return http.StatusInternalServerError
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-gost/gost/pkg/auth"
|
||||
"github.com/go-gost/gost/pkg/logger"
|
||||
)
|
||||
|
||||
func mwLogger() gin.HandlerFunc {
|
||||
return func(ctx *gin.Context) {
|
||||
// start time
|
||||
startTime := time.Now()
|
||||
// Processing request
|
||||
ctx.Next()
|
||||
duration := time.Since(startTime)
|
||||
|
||||
logger.Default().WithFields(map[string]interface{}{
|
||||
"kind": "api",
|
||||
"method": ctx.Request.Method,
|
||||
"uri": ctx.Request.RequestURI,
|
||||
"code": ctx.Writer.Status(),
|
||||
"client": ctx.ClientIP(),
|
||||
"duration": duration,
|
||||
}).Infof("| %3d | %13v | %15s | %-7s %s",
|
||||
ctx.Writer.Status(), duration, ctx.ClientIP(), ctx.Request.Method, ctx.Request.RequestURI)
|
||||
}
|
||||
}
|
||||
|
||||
func mwBasicAuth(auther auth.Authenticator) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if auther == nil {
|
||||
return
|
||||
}
|
||||
u, p, _ := c.Request.BasicAuth()
|
||||
if !auther.Authenticate(u, p) {
|
||||
c.AbortWithStatus(http.StatusUnauthorized)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-contrib/cors"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-gost/gost/pkg/auth"
|
||||
)
|
||||
|
||||
type options struct {
|
||||
accessLog bool
|
||||
pathPrefix string
|
||||
auther auth.Authenticator
|
||||
}
|
||||
|
||||
type Option func(*options)
|
||||
|
||||
func PathPrefixOption(pathPrefix string) Option {
|
||||
return func(o *options) {
|
||||
o.pathPrefix = pathPrefix
|
||||
}
|
||||
}
|
||||
|
||||
func AccessLogOption(enable bool) Option {
|
||||
return func(o *options) {
|
||||
o.accessLog = enable
|
||||
}
|
||||
}
|
||||
|
||||
func AutherOption(auther auth.Authenticator) Option {
|
||||
return func(o *options) {
|
||||
o.auther = auther
|
||||
}
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
s *http.Server
|
||||
ln net.Listener
|
||||
}
|
||||
|
||||
func NewServer(addr string, opts ...Option) (*Server, error) {
|
||||
ln, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var options options
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
|
||||
r := gin.New()
|
||||
r.Use(
|
||||
cors.New((cors.Config{
|
||||
AllowAllOrigins: true,
|
||||
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
|
||||
AllowHeaders: []string{"*"},
|
||||
})),
|
||||
gin.Recovery(),
|
||||
)
|
||||
if options.accessLog {
|
||||
r.Use(mwLogger())
|
||||
}
|
||||
|
||||
router := r.Group("")
|
||||
if options.pathPrefix != "" {
|
||||
router = router.Group(options.pathPrefix)
|
||||
}
|
||||
|
||||
router.StaticFS("/docs", http.FS(swaggerDoc))
|
||||
|
||||
config := router.Group("/config")
|
||||
config.Use(mwBasicAuth(options.auther))
|
||||
registerConfig(config)
|
||||
|
||||
return &Server{
|
||||
s: &http.Server{
|
||||
Handler: r,
|
||||
},
|
||||
ln: ln,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) Serve() error {
|
||||
return s.s.Serve(s.ln)
|
||||
}
|
||||
|
||||
func (s *Server) Addr() net.Addr {
|
||||
return s.ln.Addr()
|
||||
}
|
||||
|
||||
func (s *Server) Close() error {
|
||||
return s.s.Close()
|
||||
}
|
||||
|
||||
func registerConfig(config *gin.RouterGroup) {
|
||||
config.GET("", getConfig)
|
||||
config.POST("", saveConfig)
|
||||
|
||||
config.POST("/services", createService)
|
||||
config.PUT("/services/:service", updateService)
|
||||
config.DELETE("/services/:service", deleteService)
|
||||
|
||||
config.POST("/chains", createChain)
|
||||
config.PUT("/chains/:chain", updateChain)
|
||||
config.DELETE("/chains/:chain", deleteChain)
|
||||
|
||||
config.POST("/authers", createAuther)
|
||||
config.PUT("/authers/:auther", updateAuther)
|
||||
config.DELETE("/authers/:auther", deleteAuther)
|
||||
|
||||
config.POST("/admissions", createAdmission)
|
||||
config.PUT("/admissions/:admission", updateAdmission)
|
||||
config.DELETE("/admissions/:admission", deleteAdmission)
|
||||
|
||||
config.POST("/bypasses", createBypass)
|
||||
config.PUT("/bypasses/:bypass", updateBypass)
|
||||
config.DELETE("/bypasses/:bypass", deleteBypass)
|
||||
|
||||
config.POST("/resolvers", createResolver)
|
||||
config.PUT("/resolvers/:resolver", updateResolver)
|
||||
config.DELETE("/resolvers/:resolver", deleteResolver)
|
||||
|
||||
config.POST("/hosts", createHosts)
|
||||
config.PUT("/hosts/:hosts", updateHosts)
|
||||
config.DELETE("/hosts/:hosts", deleteHosts)
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+12
-104
@@ -3,131 +3,39 @@ package bypass
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/go-gost/gost/pkg/common/matcher"
|
||||
"github.com/go-gost/gost/pkg/logger"
|
||||
glob "github.com/gobwas/glob"
|
||||
)
|
||||
|
||||
// Matcher is a generic pattern matcher,
|
||||
// it gives the match result of the given pattern for specific v.
|
||||
type Matcher interface {
|
||||
Match(v string) bool
|
||||
}
|
||||
|
||||
// NewMatcher creates a Matcher for the given pattern.
|
||||
// The acutal Matcher depends on the pattern:
|
||||
// IP Matcher if pattern is a valid IP address.
|
||||
// CIDR Matcher if pattern is a valid CIDR address.
|
||||
// Domain Matcher if both of the above are not.
|
||||
func NewMatcher(pattern string) Matcher {
|
||||
if pattern == "" {
|
||||
return nil
|
||||
}
|
||||
if ip := net.ParseIP(pattern); ip != nil {
|
||||
return IPMatcher(ip)
|
||||
}
|
||||
if _, inet, err := net.ParseCIDR(pattern); err == nil {
|
||||
return CIDRMatcher(inet)
|
||||
}
|
||||
return DomainMatcher(pattern)
|
||||
}
|
||||
|
||||
type ipMatcher struct {
|
||||
ip net.IP
|
||||
}
|
||||
|
||||
// IPMatcher creates a Matcher for a specific IP address.
|
||||
func IPMatcher(ip net.IP) Matcher {
|
||||
return &ipMatcher{
|
||||
ip: ip,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ipMatcher) Match(ip string) bool {
|
||||
if m == nil {
|
||||
return false
|
||||
}
|
||||
return m.ip.Equal(net.ParseIP(ip))
|
||||
}
|
||||
|
||||
type cidrMatcher struct {
|
||||
ipNet *net.IPNet
|
||||
}
|
||||
|
||||
// CIDRMatcher creates a Matcher for a specific CIDR notation IP address.
|
||||
func CIDRMatcher(inet *net.IPNet) Matcher {
|
||||
return &cidrMatcher{
|
||||
ipNet: inet,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *cidrMatcher) Match(ip string) bool {
|
||||
if m == nil || m.ipNet == nil {
|
||||
return false
|
||||
}
|
||||
return m.ipNet.Contains(net.ParseIP(ip))
|
||||
}
|
||||
|
||||
type domainMatcher struct {
|
||||
pattern string
|
||||
glob glob.Glob
|
||||
}
|
||||
|
||||
// DomainMatcher creates a Matcher for a specific domain pattern,
|
||||
// the pattern can be a plain domain such as 'example.com',
|
||||
// a wildcard such as '*.exmaple.com' or a special wildcard '.example.com'.
|
||||
func DomainMatcher(pattern string) Matcher {
|
||||
p := pattern
|
||||
if strings.HasPrefix(pattern, ".") {
|
||||
p = pattern[1:] // trim the prefix '.'
|
||||
pattern = "*" + p
|
||||
}
|
||||
return &domainMatcher{
|
||||
pattern: p,
|
||||
glob: glob.MustCompile(pattern),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *domainMatcher) Match(domain string) bool {
|
||||
if m == nil || m.glob == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if domain == m.pattern {
|
||||
return true
|
||||
}
|
||||
return m.glob.Match(domain)
|
||||
}
|
||||
|
||||
// Bypass is a filter of address (IP or domain).
|
||||
type Bypass interface {
|
||||
// Contains reports whether the bypass includes addr.
|
||||
Contains(addr string) bool
|
||||
}
|
||||
|
||||
type bypassOptions struct {
|
||||
type options struct {
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
type BypassOption func(opts *bypassOptions)
|
||||
type Option func(opts *options)
|
||||
|
||||
func LoggerBypassOption(logger logger.Logger) BypassOption {
|
||||
return func(opts *bypassOptions) {
|
||||
func LoggerOption(logger logger.Logger) Option {
|
||||
return func(opts *options) {
|
||||
opts.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
type bypass struct {
|
||||
matchers []Matcher
|
||||
matchers []matcher.Matcher
|
||||
reversed bool
|
||||
options bypassOptions
|
||||
options options
|
||||
}
|
||||
|
||||
// NewBypass creates and initializes a new Bypass using matchers as its match rules.
|
||||
// The rules will be reversed if the reversed is true.
|
||||
func NewBypass(reversed bool, matchers []Matcher, opts ...BypassOption) Bypass {
|
||||
options := bypassOptions{}
|
||||
func NewBypass(reversed bool, matchers []matcher.Matcher, opts ...Option) Bypass {
|
||||
options := options{}
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
@@ -140,10 +48,10 @@ func NewBypass(reversed bool, matchers []Matcher, opts ...BypassOption) Bypass {
|
||||
|
||||
// NewBypassPatterns creates and initializes a new Bypass using matcher patterns as its match rules.
|
||||
// The rules will be reversed if the reverse is true.
|
||||
func NewBypassPatterns(reversed bool, patterns []string, opts ...BypassOption) Bypass {
|
||||
var matchers []Matcher
|
||||
func NewBypassPatterns(reversed bool, patterns []string, opts ...Option) Bypass {
|
||||
var matchers []matcher.Matcher
|
||||
for _, pattern := range patterns {
|
||||
if m := NewMatcher(pattern); m != nil {
|
||||
if m := matcher.NewMatcher(pattern); m != nil {
|
||||
matchers = append(matchers, m)
|
||||
}
|
||||
}
|
||||
|
||||
+8
-12
@@ -1,5 +1,9 @@
|
||||
package chain
|
||||
|
||||
type Chainer interface {
|
||||
Route(network, address string) *Route
|
||||
}
|
||||
|
||||
type Chain struct {
|
||||
groups []*NodeGroup
|
||||
}
|
||||
@@ -8,16 +12,12 @@ func (c *Chain) AddNodeGroup(group *NodeGroup) {
|
||||
c.groups = append(c.groups, group)
|
||||
}
|
||||
|
||||
func (c *Chain) GetRoute() (r *route) {
|
||||
return c.GetRouteFor("tcp", "")
|
||||
}
|
||||
|
||||
func (c *Chain) GetRouteFor(network, address string) (r *route) {
|
||||
func (c *Chain) Route(network, address string) (r *Route) {
|
||||
if c == nil || len(c.groups) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
r = &route{}
|
||||
r = &Route{}
|
||||
for _, group := range c.groups {
|
||||
node := group.Next()
|
||||
if node == nil {
|
||||
@@ -32,14 +32,10 @@ func (c *Chain) GetRouteFor(network, address string) (r *route) {
|
||||
WithRoute(r)
|
||||
node = node.Copy()
|
||||
node.Transport = tr
|
||||
r = &route{}
|
||||
r = &Route{}
|
||||
}
|
||||
|
||||
r.AddNode(node)
|
||||
r.addNode(node)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (c *Chain) IsEmpty() bool {
|
||||
return c == nil || len(c.groups) == 0
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/go-gost/gost/pkg/resolver"
|
||||
)
|
||||
|
||||
func resolve(ctx context.Context, addr string, resolver resolver.Resolver, hosts hosts.HostMapper, log logger.Logger) (string, error) {
|
||||
func resolve(ctx context.Context, network, addr string, r resolver.Resolver, hosts hosts.HostMapper, log logger.Logger) (string, error) {
|
||||
if addr == "" {
|
||||
return addr, nil
|
||||
}
|
||||
@@ -24,15 +24,18 @@ func resolve(ctx context.Context, addr string, resolver resolver.Resolver, hosts
|
||||
}
|
||||
|
||||
if hosts != nil {
|
||||
if ips, _ := hosts.Lookup("ip", host); len(ips) > 0 {
|
||||
if ips, _ := hosts.Lookup(network, host); len(ips) > 0 {
|
||||
log.Debugf("hit host mapper: %s -> %s", host, ips)
|
||||
return net.JoinHostPort(ips[0].String(), port), nil
|
||||
}
|
||||
}
|
||||
|
||||
if resolver != nil {
|
||||
ips, err := resolver.Resolve(ctx, host)
|
||||
if r != nil {
|
||||
ips, err := r.Resolve(ctx, network, host)
|
||||
if err != nil {
|
||||
if err == resolver.ErrInvalid {
|
||||
return addr, nil
|
||||
}
|
||||
log.Error(err)
|
||||
}
|
||||
if len(ips) == 0 {
|
||||
|
||||
+24
-20
@@ -15,17 +15,17 @@ var (
|
||||
ErrEmptyRoute = errors.New("empty route")
|
||||
)
|
||||
|
||||
type route struct {
|
||||
type Route struct {
|
||||
nodes []*Node
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
func (r *route) AddNode(node *Node) {
|
||||
func (r *Route) addNode(node *Node) {
|
||||
r.nodes = append(r.nodes, node)
|
||||
}
|
||||
|
||||
func (r *route) Dial(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
if r.IsEmpty() {
|
||||
func (r *Route) Dial(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
if r.Len() == 0 {
|
||||
return r.dialDirect(ctx, network, address)
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ func (r *route) Dial(ctx context.Context, network, address string) (net.Conn, er
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cc, err := r.Last().Transport.Connect(ctx, conn, network, address)
|
||||
cc, err := r.GetNode(r.Len()-1).Transport.Connect(ctx, conn, network, address)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
@@ -42,7 +42,7 @@ func (r *route) Dial(ctx context.Context, network, address string) (net.Conn, er
|
||||
return cc, nil
|
||||
}
|
||||
|
||||
func (r *route) dialDirect(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
func (r *Route) dialDirect(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
switch network {
|
||||
case "udp", "udp4", "udp6":
|
||||
if address == "" {
|
||||
@@ -55,8 +55,8 @@ func (r *route) dialDirect(ctx context.Context, network, address string) (net.Co
|
||||
return d.DialContext(ctx, network, address)
|
||||
}
|
||||
|
||||
func (r *route) Bind(ctx context.Context, network, address string, opts ...connector.BindOption) (net.Listener, error) {
|
||||
if r.IsEmpty() {
|
||||
func (r *Route) Bind(ctx context.Context, network, address string, opts ...connector.BindOption) (net.Listener, error) {
|
||||
if r.Len() == 0 {
|
||||
return r.bindLocal(ctx, network, address, opts...)
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ func (r *route) Bind(ctx context.Context, network, address string, opts ...conne
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ln, err := r.Last().Transport.Bind(ctx, conn, network, address, opts...)
|
||||
ln, err := r.GetNode(r.Len()-1).Transport.Bind(ctx, conn, network, address, opts...)
|
||||
if err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
@@ -74,14 +74,15 @@ func (r *route) Bind(ctx context.Context, network, address string, opts ...conne
|
||||
return ln, nil
|
||||
}
|
||||
|
||||
func (r *route) connect(ctx context.Context) (conn net.Conn, err error) {
|
||||
if r.IsEmpty() {
|
||||
func (r *Route) connect(ctx context.Context) (conn net.Conn, err error) {
|
||||
if r.Len() == 0 {
|
||||
return nil, ErrEmptyRoute
|
||||
}
|
||||
|
||||
network := "ip"
|
||||
node := r.nodes[0]
|
||||
|
||||
addr, err := resolve(ctx, node.Addr, node.Resolver, node.Hosts, r.logger)
|
||||
addr, err := resolve(ctx, network, node.Addr, node.Resolver, node.Hosts, r.logger)
|
||||
if err != nil {
|
||||
node.Marker.Mark()
|
||||
return
|
||||
@@ -102,7 +103,7 @@ func (r *route) connect(ctx context.Context) (conn net.Conn, err error) {
|
||||
|
||||
preNode := node
|
||||
for _, node := range r.nodes[1:] {
|
||||
addr, err = resolve(ctx, node.Addr, node.Resolver, node.Hosts, r.logger)
|
||||
addr, err = resolve(ctx, network, node.Addr, node.Resolver, node.Hosts, r.logger)
|
||||
if err != nil {
|
||||
cn.Close()
|
||||
node.Marker.Mark()
|
||||
@@ -130,18 +131,21 @@ func (r *route) connect(ctx context.Context) (conn net.Conn, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (r *route) IsEmpty() bool {
|
||||
return r == nil || len(r.nodes) == 0
|
||||
func (r *Route) Len() int {
|
||||
if r == nil {
|
||||
return 0
|
||||
}
|
||||
return len(r.nodes)
|
||||
}
|
||||
|
||||
func (r *route) Last() *Node {
|
||||
if r.IsEmpty() {
|
||||
func (r *Route) GetNode(index int) *Node {
|
||||
if r.Len() == 0 || index < 0 || index >= len(r.nodes) {
|
||||
return nil
|
||||
}
|
||||
return r.nodes[len(r.nodes)-1]
|
||||
return r.nodes[index]
|
||||
}
|
||||
|
||||
func (r *route) Path() (path []*Node) {
|
||||
func (r *Route) Path() (path []*Node) {
|
||||
if r == nil || len(r.nodes) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -155,7 +159,7 @@ func (r *route) Path() (path []*Node) {
|
||||
return
|
||||
}
|
||||
|
||||
func (r *route) bindLocal(ctx context.Context, network, address string, opts ...connector.BindOption) (net.Listener, error) {
|
||||
func (r *Route) bindLocal(ctx context.Context, network, address string, opts ...connector.BindOption) (net.Listener, error) {
|
||||
options := connector.BindOptions{}
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
|
||||
+10
-4
@@ -14,7 +14,7 @@ import (
|
||||
|
||||
type Router struct {
|
||||
Retries int
|
||||
Chain *Chain
|
||||
Chain Chainer
|
||||
Hosts hosts.HostMapper
|
||||
Resolver resolver.Resolver
|
||||
Logger logger.Logger
|
||||
@@ -41,7 +41,10 @@ func (r *Router) dial(ctx context.Context, network, address string) (conn net.Co
|
||||
r.Logger.Debugf("dial %s/%s", address, network)
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
route := r.Chain.GetRouteFor(network, address)
|
||||
var route *Route
|
||||
if r.Chain != nil {
|
||||
route = r.Chain.Route(network, address)
|
||||
}
|
||||
|
||||
if r.Logger.IsLevelEnabled(logger.DebugLevel) {
|
||||
buf := bytes.Buffer{}
|
||||
@@ -52,7 +55,7 @@ func (r *Router) dial(ctx context.Context, network, address string) (conn net.Co
|
||||
r.Logger.Debugf("route(retry=%d) %s", i, buf.String())
|
||||
}
|
||||
|
||||
address, err = resolve(ctx, address, r.Resolver, r.Hosts, r.Logger)
|
||||
address, err = resolve(ctx, "ip", address, r.Resolver, r.Hosts, r.Logger)
|
||||
if err != nil {
|
||||
r.Logger.Error(err)
|
||||
break
|
||||
@@ -80,7 +83,10 @@ func (r *Router) Bind(ctx context.Context, network, address string, opts ...conn
|
||||
r.Logger.Debugf("bind on %s/%s", address, network)
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
route := r.Chain.GetRouteFor(network, address)
|
||||
var route *Route
|
||||
if r.Chain != nil {
|
||||
route = r.Chain.Route(network, address)
|
||||
}
|
||||
|
||||
if r.Logger.IsLevelEnabled(logger.DebugLevel) {
|
||||
buf := bytes.Buffer{}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
type Transport struct {
|
||||
addr string
|
||||
route *route
|
||||
route *Route
|
||||
dialer dialer.Dialer
|
||||
connector connector.Connector
|
||||
}
|
||||
@@ -39,7 +39,7 @@ func (tr *Transport) dialOptions() []dialer.DialOption {
|
||||
opts := []dialer.DialOption{
|
||||
dialer.HostDialOption(tr.addr),
|
||||
}
|
||||
if !tr.route.IsEmpty() {
|
||||
if tr.route.Len() > 0 {
|
||||
opts = append(opts,
|
||||
dialer.DialFuncDialOption(
|
||||
func(ctx context.Context, addr string) (net.Conn, error) {
|
||||
@@ -84,7 +84,7 @@ func (tr *Transport) Multiplex() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (tr *Transport) WithRoute(r *route) *Transport {
|
||||
func (tr *Transport) WithRoute(r *Route) *Transport {
|
||||
tr.route = r
|
||||
return tr
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package matcher
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/gobwas/glob"
|
||||
)
|
||||
|
||||
// Matcher is a generic pattern matcher,
|
||||
// it gives the match result of the given pattern for specific v.
|
||||
type Matcher interface {
|
||||
Match(v string) bool
|
||||
}
|
||||
|
||||
// NewMatcher creates a Matcher for the given pattern.
|
||||
// The acutal Matcher depends on the pattern:
|
||||
// IP Matcher if pattern is a valid IP address.
|
||||
// CIDR Matcher if pattern is a valid CIDR address.
|
||||
// Domain Matcher if both of the above are not.
|
||||
func NewMatcher(pattern string) Matcher {
|
||||
if pattern == "" {
|
||||
return nil
|
||||
}
|
||||
if ip := net.ParseIP(pattern); ip != nil {
|
||||
return IPMatcher(ip)
|
||||
}
|
||||
if _, inet, err := net.ParseCIDR(pattern); err == nil {
|
||||
return CIDRMatcher(inet)
|
||||
}
|
||||
return DomainMatcher(pattern)
|
||||
}
|
||||
|
||||
type ipMatcher struct {
|
||||
ip net.IP
|
||||
}
|
||||
|
||||
// IPMatcher creates a Matcher for a specific IP address.
|
||||
func IPMatcher(ip net.IP) Matcher {
|
||||
return &ipMatcher{
|
||||
ip: ip,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *ipMatcher) Match(ip string) bool {
|
||||
if m == nil {
|
||||
return false
|
||||
}
|
||||
return m.ip.Equal(net.ParseIP(ip))
|
||||
}
|
||||
|
||||
type cidrMatcher struct {
|
||||
ipNet *net.IPNet
|
||||
}
|
||||
|
||||
// CIDRMatcher creates a Matcher for a specific CIDR notation IP address.
|
||||
func CIDRMatcher(inet *net.IPNet) Matcher {
|
||||
return &cidrMatcher{
|
||||
ipNet: inet,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *cidrMatcher) Match(ip string) bool {
|
||||
if m == nil || m.ipNet == nil {
|
||||
return false
|
||||
}
|
||||
return m.ipNet.Contains(net.ParseIP(ip))
|
||||
}
|
||||
|
||||
type domainMatcher struct {
|
||||
pattern string
|
||||
glob glob.Glob
|
||||
}
|
||||
|
||||
// DomainMatcher creates a Matcher for a specific domain pattern,
|
||||
// the pattern can be a plain domain such as 'example.com',
|
||||
// a wildcard such as '*.exmaple.com' or a special wildcard '.example.com'.
|
||||
func DomainMatcher(pattern string) Matcher {
|
||||
p := pattern
|
||||
if strings.HasPrefix(pattern, ".") {
|
||||
p = pattern[1:] // trim the prefix '.'
|
||||
pattern = "*" + p
|
||||
}
|
||||
return &domainMatcher{
|
||||
pattern: p,
|
||||
glob: glob.MustCompile(pattern),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *domainMatcher) Match(domain string) bool {
|
||||
if m == nil || m.glob == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if domain == m.pattern {
|
||||
return true
|
||||
}
|
||||
return m.glob.Match(domain)
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/go-gost/gost/pkg/auth"
|
||||
)
|
||||
|
||||
func AuthFromUsers(users ...*url.Userinfo) auth.Authenticator {
|
||||
kvs := make(map[string]string)
|
||||
for _, v := range users {
|
||||
if v == nil || v.Username() == "" {
|
||||
continue
|
||||
}
|
||||
kvs[v.Username()], _ = v.Password()
|
||||
}
|
||||
|
||||
var authenticator auth.Authenticator
|
||||
if len(kvs) > 0 {
|
||||
authenticator = auth.NewMapAuthenticator(kvs)
|
||||
}
|
||||
|
||||
return authenticator
|
||||
}
|
||||
+165
-89
@@ -1,7 +1,9 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
@@ -19,149 +21,213 @@ func init() {
|
||||
v.AddConfigPath(".")
|
||||
}
|
||||
|
||||
var (
|
||||
global = &Config{}
|
||||
globalMux sync.RWMutex
|
||||
)
|
||||
|
||||
func Global() *Config {
|
||||
globalMux.RLock()
|
||||
defer globalMux.RUnlock()
|
||||
|
||||
cfg := &Config{}
|
||||
*cfg = *global
|
||||
return cfg
|
||||
}
|
||||
|
||||
func SetGlobal(c *Config) {
|
||||
globalMux.Lock()
|
||||
defer globalMux.Unlock()
|
||||
|
||||
global = c
|
||||
}
|
||||
|
||||
type LogConfig struct {
|
||||
Output string `yaml:",omitempty"`
|
||||
Level string `yaml:",omitempty"`
|
||||
Format string `yaml:",omitempty"`
|
||||
Output string `yaml:",omitempty" json:"output,omitempty"`
|
||||
Level string `yaml:",omitempty" json:"level,omitempty"`
|
||||
Format string `yaml:",omitempty" json:"format,omitempty"`
|
||||
}
|
||||
|
||||
type ProfilingConfig struct {
|
||||
Addr string
|
||||
Enabled bool
|
||||
Addr string `json:"addr"`
|
||||
Enable bool `json:"enable"`
|
||||
}
|
||||
|
||||
type APIConfig struct {
|
||||
Addr string `json:"addr"`
|
||||
PathPrefix string `yaml:"pathPrefix,omitempty" json:"pathPrefix,omitempty"`
|
||||
AccessLog bool `yaml:"accesslog,omitempty" json:"accesslog,omitempty"`
|
||||
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
|
||||
Auther string `yaml:",omitempty" json:"auther,omitempty"`
|
||||
}
|
||||
|
||||
type MetricsConfig struct {
|
||||
Enable bool `json:"enable"`
|
||||
Addr string `json:"addr"`
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
type TLSConfig struct {
|
||||
CertFile string `yaml:"certFile,omitempty"`
|
||||
KeyFile string `yaml:"keyFile,omitempty"`
|
||||
CAFile string `yaml:"caFile,omitempty"`
|
||||
Secure bool `yaml:",omitempty"`
|
||||
ServerName string `yaml:"serverName,omitempty"`
|
||||
CertFile string `yaml:"certFile,omitempty" json:"certFile,omitempty"`
|
||||
KeyFile string `yaml:"keyFile,omitempty" json:"keyFile,omitempty"`
|
||||
CAFile string `yaml:"caFile,omitempty" json:"caFile,omitempty"`
|
||||
Secure bool `yaml:",omitempty" json:"secure,omitempty"`
|
||||
ServerName string `yaml:"serverName,omitempty" json:"serverName,omitempty"`
|
||||
}
|
||||
|
||||
type AutherConfig struct {
|
||||
Name string `json:"name"`
|
||||
// inline, file, redis, etc.
|
||||
Type string `yaml:",omitempty" json:"type,omitempty"`
|
||||
Auths []*AuthConfig `yaml:",omitempty" json:"auths"`
|
||||
// File string `yaml:",omitempty" json:"file"`
|
||||
}
|
||||
|
||||
type AuthConfig struct {
|
||||
Username string
|
||||
Password string
|
||||
Username string `json:"username"`
|
||||
Password string `yaml:",omitempty" json:"password,omitempty"`
|
||||
}
|
||||
|
||||
type SelectorConfig struct {
|
||||
Strategy string
|
||||
MaxFails int `yaml:"maxFails"`
|
||||
FailTimeout time.Duration `yaml:"failTimeout"`
|
||||
Strategy string `json:"strategy"`
|
||||
MaxFails int `yaml:"maxFails" json:"maxFails"`
|
||||
FailTimeout time.Duration `yaml:"failTimeout" json:"failTimeout"`
|
||||
}
|
||||
|
||||
type AdmissionConfig struct {
|
||||
Name string `json:"name"`
|
||||
// inline, file, etc.
|
||||
Type string `yaml:",omitempty" json:"type,omitempty"`
|
||||
Reverse bool `yaml:",omitempty" json:"reverse,omitempty"`
|
||||
Matchers []string `json:"matchers"`
|
||||
}
|
||||
|
||||
type BypassConfig struct {
|
||||
Name string
|
||||
Reverse bool `yaml:",omitempty"`
|
||||
Matchers []string
|
||||
Name string `json:"name"`
|
||||
// inline, file, etc.
|
||||
Type string `yaml:",omitempty" json:"type,omitempty"`
|
||||
Reverse bool `yaml:",omitempty" json:"reverse,omitempty"`
|
||||
Matchers []string `json:"matchers"`
|
||||
}
|
||||
|
||||
type NameserverConfig struct {
|
||||
Addr string
|
||||
Chain string `yaml:",omitempty"`
|
||||
Prefer string `yaml:",omitempty"`
|
||||
ClientIP string `yaml:"clientIP,omitempty"`
|
||||
Hostname string `yaml:",omitempty"`
|
||||
TTL time.Duration `yaml:",omitempty"`
|
||||
Timeout time.Duration `yaml:",omitempty"`
|
||||
Addr string `json:"addr"`
|
||||
Chain string `yaml:",omitempty" json:"chain,omitempty"`
|
||||
Prefer string `yaml:",omitempty" json:"prefer,omitempty"`
|
||||
ClientIP string `yaml:"clientIP,omitempty" json:"clientIP,omitempty"`
|
||||
Hostname string `yaml:",omitempty" json:"hostname,omitempty"`
|
||||
TTL time.Duration `yaml:",omitempty" json:"ttl,omitempty"`
|
||||
Timeout time.Duration `yaml:",omitempty" json:"timeout,omitempty"`
|
||||
}
|
||||
|
||||
type ResolverConfig struct {
|
||||
Name string
|
||||
Nameservers []NameserverConfig
|
||||
Name string `json:"name"`
|
||||
// inline, file, etc.
|
||||
Type string `yaml:",omitempty" json:"type,omitempty"`
|
||||
Nameservers []*NameserverConfig `json:"nameservers"`
|
||||
}
|
||||
|
||||
type HostMappingConfig struct {
|
||||
IP string
|
||||
Hostname string
|
||||
Aliases []string `yaml:",omitempty"`
|
||||
IP string `json:"ip"`
|
||||
Hostname string `json:"hostname"`
|
||||
Aliases []string `yaml:",omitempty" json:"aliases,omitempty"`
|
||||
}
|
||||
|
||||
type HostsConfig struct {
|
||||
Name string
|
||||
Mappings []HostMappingConfig
|
||||
Name string `json:"name"`
|
||||
// inline, file, etc.
|
||||
Type string `yaml:",omitempty" json:"type,omitempty"`
|
||||
Mappings []*HostMappingConfig `json:"mappings"`
|
||||
}
|
||||
|
||||
type ListenerConfig struct {
|
||||
Type string
|
||||
Chain string `yaml:",omitempty"`
|
||||
Auths []*AuthConfig `yaml:",omitempty"`
|
||||
TLS *TLSConfig `yaml:",omitempty"`
|
||||
Metadata map[string]interface{} `yaml:",omitempty"`
|
||||
Type string `json:"type"`
|
||||
Chain string `yaml:",omitempty" json:"chain,omitempty"`
|
||||
Auther string `yaml:",omitempty" json:"auther,omitempty"`
|
||||
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
|
||||
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
|
||||
Metadata map[string]interface{} `yaml:",omitempty" json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
type HandlerConfig struct {
|
||||
Type string
|
||||
Retries int `yaml:",omitempty"`
|
||||
Chain string `yaml:",omitempty"`
|
||||
Auths []*AuthConfig `yaml:",omitempty"`
|
||||
TLS *TLSConfig `yaml:",omitempty"`
|
||||
Metadata map[string]interface{} `yaml:",omitempty"`
|
||||
Type string `json:"type"`
|
||||
Retries int `yaml:",omitempty" json:"retries,omitempty"`
|
||||
Chain string `yaml:",omitempty" json:"chain,omitempty"`
|
||||
Auther string `yaml:",omitempty" json:"auther,omitempty"`
|
||||
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
|
||||
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
|
||||
Metadata map[string]interface{} `yaml:",omitempty" json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
type ForwarderConfig struct {
|
||||
Targets []string
|
||||
Selector *SelectorConfig `yaml:",omitempty"`
|
||||
Targets []string `json:"targets"`
|
||||
Selector *SelectorConfig `yaml:",omitempty" json:"selector,omitempty"`
|
||||
}
|
||||
|
||||
type DialerConfig struct {
|
||||
Type string
|
||||
Auth *AuthConfig `yaml:",omitempty"`
|
||||
TLS *TLSConfig `yaml:",omitempty"`
|
||||
Metadata map[string]interface{} `yaml:",omitempty"`
|
||||
Type string `json:"type"`
|
||||
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
|
||||
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
|
||||
Metadata map[string]interface{} `yaml:",omitempty" json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
type ConnectorConfig struct {
|
||||
Type string
|
||||
Auth *AuthConfig `yaml:",omitempty"`
|
||||
TLS *TLSConfig `yaml:",omitempty"`
|
||||
Metadata map[string]interface{} `yaml:",omitempty"`
|
||||
Type string `json:"type"`
|
||||
Auth *AuthConfig `yaml:",omitempty" json:"auth,omitempty"`
|
||||
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
|
||||
Metadata map[string]interface{} `yaml:",omitempty" json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
type ServiceConfig struct {
|
||||
Name string
|
||||
Addr string `yaml:",omitempty"`
|
||||
Bypass string `yaml:",omitempty"`
|
||||
Resolver string `yaml:",omitempty"`
|
||||
Hosts string `yaml:",omitempty"`
|
||||
Handler *HandlerConfig `yaml:",omitempty"`
|
||||
Listener *ListenerConfig `yaml:",omitempty"`
|
||||
Forwarder *ForwarderConfig `yaml:",omitempty"`
|
||||
Name string `json:"name"`
|
||||
Addr string `yaml:",omitempty" json:"addr,omitempty"`
|
||||
Admission string `yaml:",omitempty" json:"admission,omitempty"`
|
||||
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
|
||||
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
|
||||
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
|
||||
Handler *HandlerConfig `yaml:",omitempty" json:"handler,omitempty"`
|
||||
Listener *ListenerConfig `yaml:",omitempty" json:"listener,omitempty"`
|
||||
Forwarder *ForwarderConfig `yaml:",omitempty" json:"forwarder,omitempty"`
|
||||
}
|
||||
|
||||
type ChainConfig struct {
|
||||
Name string
|
||||
Selector *SelectorConfig `yaml:",omitempty"`
|
||||
Hops []*HopConfig
|
||||
Name string `json:"name"`
|
||||
Selector *SelectorConfig `yaml:",omitempty" json:"selector,omitempty"`
|
||||
Hops []*HopConfig `json:"hops"`
|
||||
}
|
||||
|
||||
type HopConfig struct {
|
||||
Name string
|
||||
Selector *SelectorConfig `yaml:",omitempty"`
|
||||
Bypass string `yaml:",omitempty"`
|
||||
Resolver string `yaml:",omitempty"`
|
||||
Hosts string `yaml:",omitempty"`
|
||||
Nodes []*NodeConfig
|
||||
Name string `json:"name"`
|
||||
Selector *SelectorConfig `yaml:",omitempty" json:"selector,omitempty"`
|
||||
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
|
||||
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
|
||||
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
|
||||
Nodes []*NodeConfig `json:"nodes"`
|
||||
}
|
||||
|
||||
type NodeConfig struct {
|
||||
Name string
|
||||
Addr string `yaml:",omitempty"`
|
||||
Bypass string `yaml:",omitempty"`
|
||||
Resolver string `yaml:",omitempty"`
|
||||
Hosts string `yaml:",omitempty"`
|
||||
Connector *ConnectorConfig `yaml:",omitempty"`
|
||||
Dialer *DialerConfig `yaml:",omitempty"`
|
||||
Name string `json:"name"`
|
||||
Addr string `yaml:",omitempty" json:"addr,omitempty"`
|
||||
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
|
||||
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
|
||||
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
|
||||
Connector *ConnectorConfig `yaml:",omitempty" json:"connector,omitempty"`
|
||||
Dialer *DialerConfig `yaml:",omitempty" json:"dialer,omitempty"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Services []*ServiceConfig
|
||||
Chains []*ChainConfig `yaml:",omitempty"`
|
||||
Bypasses []*BypassConfig `yaml:",omitempty"`
|
||||
Resolvers []*ResolverConfig `yaml:",omitempty"`
|
||||
Hosts []*HostsConfig `yaml:",omitempty"`
|
||||
TLS *TLSConfig `yaml:",omitempty"`
|
||||
Log *LogConfig `yaml:",omitempty"`
|
||||
Profiling *ProfilingConfig `yaml:",omitempty"`
|
||||
Services []*ServiceConfig `json:"services"`
|
||||
Chains []*ChainConfig `yaml:",omitempty" json:"chains,omitempty"`
|
||||
Authers []*AutherConfig `yaml:",omitempty" json:"authers,omitempty"`
|
||||
Admissions []*AdmissionConfig `yaml:",omitempty" json:"admissions,omitempty"`
|
||||
Bypasses []*BypassConfig `yaml:",omitempty" json:"bypasses,omitempty"`
|
||||
Resolvers []*ResolverConfig `yaml:",omitempty" json:"resolvers,omitempty"`
|
||||
Hosts []*HostsConfig `yaml:",omitempty" json:"hosts,omitempty"`
|
||||
TLS *TLSConfig `yaml:",omitempty" json:"tls,omitempty"`
|
||||
Log *LogConfig `yaml:",omitempty" json:"log,omitempty"`
|
||||
Profiling *ProfilingConfig `yaml:",omitempty" json:"profiling,omitempty"`
|
||||
API *APIConfig `yaml:",omitempty" json:"api,omitempty"`
|
||||
Metrics *MetricsConfig `yaml:",omitempty" json:"metrics,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Config) Load() error {
|
||||
@@ -188,9 +254,19 @@ func (c *Config) ReadFile(file string) error {
|
||||
return v.Unmarshal(c)
|
||||
}
|
||||
|
||||
func (c *Config) Write(w io.Writer) error {
|
||||
enc := yaml.NewEncoder(w)
|
||||
defer enc.Close()
|
||||
func (c *Config) Write(w io.Writer, format string) error {
|
||||
switch format {
|
||||
case "json":
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetIndent("", " ")
|
||||
enc.Encode(c)
|
||||
return nil
|
||||
case "yaml":
|
||||
fallthrough
|
||||
default:
|
||||
enc := yaml.NewEncoder(w)
|
||||
defer enc.Close()
|
||||
|
||||
return enc.Encode(c)
|
||||
return enc.Encode(c)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
package parsing
|
||||
|
||||
import (
|
||||
"github.com/go-gost/gost/pkg/chain"
|
||||
tls_util "github.com/go-gost/gost/pkg/common/util/tls"
|
||||
"github.com/go-gost/gost/pkg/config"
|
||||
"github.com/go-gost/gost/pkg/connector"
|
||||
"github.com/go-gost/gost/pkg/dialer"
|
||||
"github.com/go-gost/gost/pkg/logger"
|
||||
"github.com/go-gost/gost/pkg/metadata"
|
||||
"github.com/go-gost/gost/pkg/registry"
|
||||
)
|
||||
|
||||
func ParseChain(cfg *config.ChainConfig) (chain.Chainer, error) {
|
||||
if cfg == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
chainLogger := logger.Default().WithFields(map[string]interface{}{
|
||||
"kind": "chain",
|
||||
"chain": cfg.Name,
|
||||
})
|
||||
|
||||
c := &chain.Chain{}
|
||||
selector := parseSelector(cfg.Selector)
|
||||
for _, hop := range cfg.Hops {
|
||||
group := &chain.NodeGroup{}
|
||||
for _, v := range hop.Nodes {
|
||||
nodeLogger := chainLogger.WithFields(map[string]interface{}{
|
||||
"kind": "node",
|
||||
"connector": v.Connector.Type,
|
||||
"dialer": v.Dialer.Type,
|
||||
"hop": hop.Name,
|
||||
"node": v.Name,
|
||||
})
|
||||
connectorLogger := nodeLogger.WithFields(map[string]interface{}{
|
||||
"kind": "connector",
|
||||
})
|
||||
|
||||
tlsCfg := v.Connector.TLS
|
||||
if tlsCfg == nil {
|
||||
tlsCfg = &config.TLSConfig{}
|
||||
}
|
||||
tlsConfig, err := tls_util.LoadClientConfig(
|
||||
tlsCfg.CertFile, tlsCfg.KeyFile, tlsCfg.CAFile,
|
||||
tlsCfg.Secure, tlsCfg.ServerName)
|
||||
if err != nil {
|
||||
chainLogger.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cr := registry.GetConnector(v.Connector.Type)(
|
||||
connector.AuthOption(parseAuth(v.Connector.Auth)),
|
||||
connector.TLSConfigOption(tlsConfig),
|
||||
connector.LoggerOption(connectorLogger),
|
||||
)
|
||||
|
||||
if v.Connector.Metadata == nil {
|
||||
v.Connector.Metadata = make(map[string]interface{})
|
||||
}
|
||||
if err := cr.Init(metadata.MapMetadata(v.Connector.Metadata)); err != nil {
|
||||
connectorLogger.Error("init: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dialerLogger := nodeLogger.WithFields(map[string]interface{}{
|
||||
"kind": "dialer",
|
||||
})
|
||||
|
||||
tlsCfg = v.Dialer.TLS
|
||||
if tlsCfg == nil {
|
||||
tlsCfg = &config.TLSConfig{}
|
||||
}
|
||||
tlsConfig, err = tls_util.LoadClientConfig(
|
||||
tlsCfg.CertFile, tlsCfg.KeyFile, tlsCfg.CAFile,
|
||||
tlsCfg.Secure, tlsCfg.ServerName)
|
||||
if err != nil {
|
||||
chainLogger.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := registry.GetDialer(v.Dialer.Type)(
|
||||
dialer.AuthOption(parseAuth(v.Dialer.Auth)),
|
||||
dialer.TLSConfigOption(tlsConfig),
|
||||
dialer.LoggerOption(dialerLogger),
|
||||
)
|
||||
|
||||
if v.Dialer.Metadata == nil {
|
||||
v.Dialer.Metadata = make(map[string]interface{})
|
||||
}
|
||||
if err := d.Init(metadata.MapMetadata(v.Dialer.Metadata)); err != nil {
|
||||
dialerLogger.Error("init: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tr := (&chain.Transport{}).
|
||||
WithConnector(cr).
|
||||
WithDialer(d).
|
||||
WithAddr(v.Addr)
|
||||
|
||||
if v.Bypass == "" {
|
||||
v.Bypass = hop.Bypass
|
||||
}
|
||||
if v.Resolver == "" {
|
||||
v.Resolver = hop.Resolver
|
||||
}
|
||||
if v.Hosts == "" {
|
||||
v.Hosts = hop.Hosts
|
||||
}
|
||||
|
||||
node := &chain.Node{
|
||||
Name: v.Name,
|
||||
Addr: v.Addr,
|
||||
Transport: tr,
|
||||
Bypass: registry.Bypass().Get(v.Bypass),
|
||||
Resolver: registry.Resolver().Get(v.Resolver),
|
||||
Hosts: registry.Hosts().Get(v.Hosts),
|
||||
Marker: &chain.FailMarker{},
|
||||
}
|
||||
group.AddNode(node)
|
||||
}
|
||||
|
||||
sel := selector
|
||||
if s := parseSelector(hop.Selector); s != nil {
|
||||
sel = s
|
||||
}
|
||||
group.WithSelector(sel)
|
||||
c.AddNodeGroup(group)
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
package parsing
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/url"
|
||||
|
||||
"github.com/go-gost/gost/pkg/admission"
|
||||
"github.com/go-gost/gost/pkg/auth"
|
||||
"github.com/go-gost/gost/pkg/bypass"
|
||||
"github.com/go-gost/gost/pkg/chain"
|
||||
"github.com/go-gost/gost/pkg/config"
|
||||
hostspkg "github.com/go-gost/gost/pkg/hosts"
|
||||
"github.com/go-gost/gost/pkg/logger"
|
||||
"github.com/go-gost/gost/pkg/registry"
|
||||
"github.com/go-gost/gost/pkg/resolver"
|
||||
resolver_impl "github.com/go-gost/gost/pkg/resolver/impl"
|
||||
)
|
||||
|
||||
func ParseAuther(cfg *config.AutherConfig) auth.Authenticator {
|
||||
if cfg == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
m := make(map[string]string)
|
||||
|
||||
for _, user := range cfg.Auths {
|
||||
if user.Username == "" {
|
||||
continue
|
||||
}
|
||||
m[user.Username] = user.Password
|
||||
}
|
||||
|
||||
if len(m) == 0 {
|
||||
return nil
|
||||
}
|
||||
return auth.NewMapAuthenticator(m)
|
||||
}
|
||||
|
||||
func ParseAutherFromAuth(au *config.AuthConfig) auth.Authenticator {
|
||||
if au == nil || au.Username == "" {
|
||||
return nil
|
||||
}
|
||||
return auth.NewMapAuthenticator(map[string]string{
|
||||
au.Username: au.Password,
|
||||
})
|
||||
}
|
||||
|
||||
func parseAuth(cfg *config.AuthConfig) *url.Userinfo {
|
||||
if cfg == nil || cfg.Username == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if cfg.Password == "" {
|
||||
return url.User(cfg.Username)
|
||||
}
|
||||
return url.UserPassword(cfg.Username, cfg.Password)
|
||||
}
|
||||
|
||||
func parseSelector(cfg *config.SelectorConfig) chain.Selector {
|
||||
if cfg == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var strategy chain.Strategy
|
||||
switch cfg.Strategy {
|
||||
case "round", "rr":
|
||||
strategy = chain.RoundRobinStrategy()
|
||||
case "random", "rand":
|
||||
strategy = chain.RandomStrategy()
|
||||
case "fifo", "ha":
|
||||
strategy = chain.FIFOStrategy()
|
||||
default:
|
||||
strategy = chain.RoundRobinStrategy()
|
||||
}
|
||||
|
||||
return chain.NewSelector(
|
||||
strategy,
|
||||
chain.InvalidFilter(),
|
||||
chain.FailFilter(cfg.MaxFails, cfg.FailTimeout),
|
||||
)
|
||||
}
|
||||
|
||||
func ParseAdmission(cfg *config.AdmissionConfig) admission.Admission {
|
||||
if cfg == nil {
|
||||
return nil
|
||||
}
|
||||
return admission.NewAdmissionPatterns(
|
||||
cfg.Reverse,
|
||||
cfg.Matchers,
|
||||
admission.LoggerOption(logger.Default().WithFields(map[string]interface{}{
|
||||
"kind": "admission",
|
||||
"admission": cfg.Name,
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
func ParseBypass(cfg *config.BypassConfig) bypass.Bypass {
|
||||
if cfg == nil {
|
||||
return nil
|
||||
}
|
||||
return bypass.NewBypassPatterns(
|
||||
cfg.Reverse,
|
||||
cfg.Matchers,
|
||||
bypass.LoggerOption(logger.Default().WithFields(map[string]interface{}{
|
||||
"kind": "bypass",
|
||||
"bypass": cfg.Name,
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
func ParseResolver(cfg *config.ResolverConfig) (resolver.Resolver, error) {
|
||||
if cfg == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var nameservers []resolver_impl.NameServer
|
||||
for _, server := range cfg.Nameservers {
|
||||
nameservers = append(nameservers, resolver_impl.NameServer{
|
||||
Addr: server.Addr,
|
||||
Chain: registry.Chain().Get(server.Chain),
|
||||
TTL: server.TTL,
|
||||
Timeout: server.Timeout,
|
||||
ClientIP: net.ParseIP(server.ClientIP),
|
||||
Prefer: server.Prefer,
|
||||
Hostname: server.Hostname,
|
||||
})
|
||||
}
|
||||
|
||||
return resolver_impl.NewResolver(
|
||||
nameservers,
|
||||
resolver_impl.LoggerResolverOption(
|
||||
logger.Default().WithFields(map[string]interface{}{
|
||||
"kind": "resolver",
|
||||
"resolver": cfg.Name,
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func ParseHosts(cfg *config.HostsConfig) hostspkg.HostMapper {
|
||||
if cfg == nil || len(cfg.Mappings) == 0 {
|
||||
return nil
|
||||
}
|
||||
hosts := hostspkg.NewHosts()
|
||||
hosts.Logger = logger.Default().WithFields(map[string]interface{}{
|
||||
"kind": "hosts",
|
||||
"hosts": cfg.Name,
|
||||
})
|
||||
|
||||
for _, host := range cfg.Mappings {
|
||||
if host.IP == "" || host.Hostname == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
ip := net.ParseIP(host.IP)
|
||||
if ip == nil {
|
||||
continue
|
||||
}
|
||||
hosts.Map(ip, host.Hostname, host.Aliases...)
|
||||
}
|
||||
return hosts
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package parsing
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-gost/gost/pkg/chain"
|
||||
tls_util "github.com/go-gost/gost/pkg/common/util/tls"
|
||||
"github.com/go-gost/gost/pkg/config"
|
||||
"github.com/go-gost/gost/pkg/handler"
|
||||
"github.com/go-gost/gost/pkg/listener"
|
||||
"github.com/go-gost/gost/pkg/logger"
|
||||
"github.com/go-gost/gost/pkg/metadata"
|
||||
"github.com/go-gost/gost/pkg/registry"
|
||||
"github.com/go-gost/gost/pkg/service"
|
||||
)
|
||||
|
||||
func ParseService(cfg *config.ServiceConfig) (service.Service, error) {
|
||||
if cfg.Listener == nil {
|
||||
cfg.Listener = &config.ListenerConfig{
|
||||
Type: "tcp",
|
||||
}
|
||||
}
|
||||
if cfg.Handler == nil {
|
||||
cfg.Handler = &config.HandlerConfig{
|
||||
Type: "auto",
|
||||
}
|
||||
}
|
||||
serviceLogger := logger.Default().WithFields(map[string]interface{}{
|
||||
"kind": "service",
|
||||
"service": cfg.Name,
|
||||
"listener": cfg.Listener.Type,
|
||||
"handler": cfg.Handler.Type,
|
||||
})
|
||||
|
||||
listenerLogger := serviceLogger.WithFields(map[string]interface{}{
|
||||
"kind": "listener",
|
||||
})
|
||||
|
||||
tlsCfg := cfg.Listener.TLS
|
||||
if tlsCfg == nil {
|
||||
tlsCfg = &config.TLSConfig{}
|
||||
}
|
||||
tlsConfig, err := tls_util.LoadServerConfig(
|
||||
tlsCfg.CertFile, tlsCfg.KeyFile, tlsCfg.CAFile)
|
||||
if err != nil {
|
||||
listenerLogger.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
auther := ParseAutherFromAuth(cfg.Listener.Auth)
|
||||
if cfg.Listener.Auther != "" {
|
||||
auther = registry.Auther().Get(cfg.Listener.Auther)
|
||||
}
|
||||
|
||||
ln := registry.GetListener(cfg.Listener.Type)(
|
||||
listener.AddrOption(cfg.Addr),
|
||||
listener.ChainOption(registry.Chain().Get(cfg.Listener.Chain)),
|
||||
listener.AutherOption(auther),
|
||||
listener.AuthOption(parseAuth(cfg.Listener.Auth)),
|
||||
listener.TLSConfigOption(tlsConfig),
|
||||
listener.LoggerOption(listenerLogger),
|
||||
)
|
||||
|
||||
if cfg.Listener.Metadata == nil {
|
||||
cfg.Listener.Metadata = make(map[string]interface{})
|
||||
}
|
||||
if err := ln.Init(metadata.MapMetadata(cfg.Listener.Metadata)); err != nil {
|
||||
listenerLogger.Error("init: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
handlerLogger := serviceLogger.WithFields(map[string]interface{}{
|
||||
"kind": "handler",
|
||||
})
|
||||
|
||||
tlsCfg = cfg.Handler.TLS
|
||||
if tlsCfg == nil {
|
||||
tlsCfg = &config.TLSConfig{}
|
||||
}
|
||||
tlsConfig, err = tls_util.LoadServerConfig(
|
||||
tlsCfg.CertFile, tlsCfg.KeyFile, tlsCfg.CAFile)
|
||||
if err != nil {
|
||||
handlerLogger.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
auther = ParseAutherFromAuth(cfg.Handler.Auth)
|
||||
if cfg.Handler.Auther != "" {
|
||||
auther = registry.Auther().Get(cfg.Handler.Auther)
|
||||
}
|
||||
h := registry.GetHandler(cfg.Handler.Type)(
|
||||
handler.AutherOption(auther),
|
||||
handler.AuthOption(parseAuth(cfg.Handler.Auth)),
|
||||
handler.RetriesOption(cfg.Handler.Retries),
|
||||
handler.ChainOption(registry.Chain().Get(cfg.Handler.Chain)),
|
||||
handler.BypassOption(registry.Bypass().Get(cfg.Bypass)),
|
||||
handler.ResolverOption(registry.Resolver().Get(cfg.Resolver)),
|
||||
handler.HostsOption(registry.Hosts().Get(cfg.Hosts)),
|
||||
handler.TLSConfigOption(tlsConfig),
|
||||
handler.LoggerOption(handlerLogger),
|
||||
)
|
||||
|
||||
if forwarder, ok := h.(handler.Forwarder); ok {
|
||||
forwarder.Forward(parseForwarder(cfg.Forwarder))
|
||||
}
|
||||
|
||||
if cfg.Handler.Metadata == nil {
|
||||
cfg.Handler.Metadata = make(map[string]interface{})
|
||||
}
|
||||
if err := h.Init(metadata.MapMetadata(cfg.Handler.Metadata)); err != nil {
|
||||
handlerLogger.Error("init: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s := service.NewService(ln, h,
|
||||
service.AdmissionOption(registry.Admission().Get(cfg.Admission)),
|
||||
service.LoggerOption(serviceLogger),
|
||||
)
|
||||
|
||||
serviceLogger.Infof("listening on %s/%s", s.Addr().String(), s.Addr().Network())
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func parseForwarder(cfg *config.ForwarderConfig) *chain.NodeGroup {
|
||||
if cfg == nil || len(cfg.Targets) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
group := &chain.NodeGroup{}
|
||||
for _, target := range cfg.Targets {
|
||||
if v := strings.TrimSpace(target); v != "" {
|
||||
group.AddNode(&chain.Node{
|
||||
Name: target,
|
||||
Addr: target,
|
||||
Marker: &chain.FailMarker{},
|
||||
})
|
||||
}
|
||||
}
|
||||
return group.WithSelector(parseSelector(cfg.Selector))
|
||||
}
|
||||
@@ -23,7 +23,6 @@ func init() {
|
||||
}
|
||||
|
||||
type httpConnector struct {
|
||||
user *url.Userinfo
|
||||
md metadata
|
||||
options connector.Options
|
||||
}
|
||||
@@ -35,7 +34,6 @@ func NewConnector(opts ...connector.Option) connector.Connector {
|
||||
}
|
||||
|
||||
return &httpConnector{
|
||||
user: options.User,
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
@@ -67,7 +65,7 @@ func (c *httpConnector) Connect(ctx context.Context, conn net.Conn, network, add
|
||||
}
|
||||
req.Header.Set("Proxy-Connection", "keep-alive")
|
||||
|
||||
if user := c.user; user != nil {
|
||||
if user := c.options.Auth; user != nil {
|
||||
u := user.Username()
|
||||
p, _ := user.Password()
|
||||
req.Header.Set("Proxy-Authorization",
|
||||
|
||||
@@ -24,7 +24,6 @@ func init() {
|
||||
}
|
||||
|
||||
type http2Connector struct {
|
||||
user *url.Userinfo
|
||||
md metadata
|
||||
options connector.Options
|
||||
}
|
||||
@@ -36,7 +35,6 @@ func NewConnector(opts ...connector.Option) connector.Connector {
|
||||
}
|
||||
|
||||
return &http2Connector{
|
||||
user: options.User,
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
@@ -76,7 +74,7 @@ func (c *http2Connector) Connect(ctx context.Context, conn net.Conn, network, ad
|
||||
req.Header.Set("User-Agent", c.md.UserAgent)
|
||||
}
|
||||
|
||||
if user := c.user; user != nil {
|
||||
if user := c.options.Auth; user != nil {
|
||||
u := user.Username()
|
||||
p, _ := user.Password()
|
||||
req.Header.Set("Proxy-Authorization",
|
||||
|
||||
@@ -9,16 +9,16 @@ import (
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
User *url.Userinfo
|
||||
Auth *url.Userinfo
|
||||
TLSConfig *tls.Config
|
||||
Logger logger.Logger
|
||||
}
|
||||
|
||||
type Option func(opts *Options)
|
||||
|
||||
func UserOption(user *url.Userinfo) Option {
|
||||
func AuthOption(auth *url.Userinfo) Option {
|
||||
return func(opts *Options) {
|
||||
opts.User = user
|
||||
opts.Auth = auth
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,10 +82,10 @@ func (c *relayConnector) bind(conn net.Conn, cmd uint8, network, address string)
|
||||
Flags: cmd,
|
||||
}
|
||||
|
||||
if c.user != nil {
|
||||
pwd, _ := c.user.Password()
|
||||
if c.options.Auth != nil {
|
||||
pwd, _ := c.options.Auth.Password()
|
||||
req.Features = append(req.Features, &relay.UserAuthFeature{
|
||||
Username: c.user.Username(),
|
||||
Username: c.options.Auth.Username(),
|
||||
Password: pwd,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/gost/pkg/common/util/socks"
|
||||
@@ -19,7 +18,6 @@ func init() {
|
||||
}
|
||||
|
||||
type relayConnector struct {
|
||||
user *url.Userinfo
|
||||
md metadata
|
||||
options connector.Options
|
||||
}
|
||||
@@ -31,7 +29,6 @@ func NewConnector(opts ...connector.Option) connector.Connector {
|
||||
}
|
||||
|
||||
return &relayConnector{
|
||||
user: options.User,
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
@@ -73,10 +70,10 @@ func (c *relayConnector) Connect(ctx context.Context, conn net.Conn, network, ad
|
||||
}
|
||||
}
|
||||
|
||||
if c.user != nil {
|
||||
pwd, _ := c.user.Password()
|
||||
if c.options.Auth != nil {
|
||||
pwd, _ := c.options.Auth.Password()
|
||||
req.Features = append(req.Features, &relay.UserAuthFeature{
|
||||
Username: c.user.Username(),
|
||||
Username: c.options.Auth.Username(),
|
||||
Password: pwd,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
@@ -21,7 +20,6 @@ func init() {
|
||||
}
|
||||
|
||||
type socks4Connector struct {
|
||||
user *url.Userinfo
|
||||
md metadata
|
||||
options connector.Options
|
||||
}
|
||||
@@ -33,7 +31,6 @@ func NewConnector(opts ...connector.Option) connector.Connector {
|
||||
}
|
||||
|
||||
return &socks4Connector{
|
||||
user: options.User,
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
@@ -99,8 +96,8 @@ func (c *socks4Connector) Connect(ctx context.Context, conn net.Conn, network, a
|
||||
}
|
||||
|
||||
var userid []byte
|
||||
if c.user != nil && c.user.Username() != "" {
|
||||
userid = []byte(c.user.Username())
|
||||
if c.options.Auth != nil {
|
||||
userid = []byte(c.options.Auth.Username())
|
||||
}
|
||||
req := gosocks4.NewRequest(gosocks4.CmdConnect, addr, userid)
|
||||
if err := req.Write(conn); err != nil {
|
||||
|
||||
@@ -48,7 +48,7 @@ func (c *socks5Connector) Init(md md.Metadata) (err error) {
|
||||
gosocks5.MethodNoAuth,
|
||||
gosocks5.MethodUserPass,
|
||||
},
|
||||
User: c.options.User,
|
||||
User: c.options.Auth,
|
||||
TLSConfig: c.options.TLSConfig,
|
||||
logger: c.options.Logger,
|
||||
}
|
||||
|
||||
@@ -41,9 +41,9 @@ func (c *ssConnector) Init(md md.Metadata) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
if c.options.User != nil {
|
||||
method := c.options.User.Username()
|
||||
password, _ := c.options.User.Password()
|
||||
if c.options.Auth != nil {
|
||||
method := c.options.Auth.Username()
|
||||
password, _ := c.options.Auth.Password()
|
||||
c.cipher, err = ss.ShadowCipher(method, password, c.md.key)
|
||||
}
|
||||
|
||||
|
||||
@@ -40,9 +40,9 @@ func (c *ssuConnector) Init(md md.Metadata) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
if c.options.User != nil {
|
||||
method := c.options.User.Username()
|
||||
password, _ := c.options.User.Password()
|
||||
if c.options.Auth != nil {
|
||||
method := c.options.Auth.Username()
|
||||
password, _ := c.options.Auth.Password()
|
||||
c.cipher, err = ss.ShadowCipher(method, password, c.md.key)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
|
||||
pb "github.com/go-gost/gost/pkg/common/util/grpc/proto"
|
||||
"github.com/go-gost/gost/pkg/dialer"
|
||||
"github.com/go-gost/gost/pkg/logger"
|
||||
md "github.com/go-gost/gost/pkg/metadata"
|
||||
"github.com/go-gost/gost/pkg/registry"
|
||||
"google.golang.org/grpc"
|
||||
@@ -24,7 +23,6 @@ func init() {
|
||||
type grpcDialer struct {
|
||||
clients map[string]pb.GostTunelClient
|
||||
clientMutex sync.Mutex
|
||||
logger logger.Logger
|
||||
md metadata
|
||||
options dialer.Options
|
||||
}
|
||||
@@ -37,7 +35,6 @@ func NewDialer(opts ...dialer.Option) dialer.Dialer {
|
||||
|
||||
return &grpcDialer{
|
||||
clients: make(map[string]pb.GostTunelClient),
|
||||
logger: options.Logger,
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
@@ -71,9 +68,12 @@ func (d *grpcDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
|
||||
if host == "" {
|
||||
host = options.Host
|
||||
}
|
||||
if h, _, _ := net.SplitHostPort(host); h != "" {
|
||||
host = h
|
||||
}
|
||||
|
||||
grpcOpts := []grpc.DialOption{
|
||||
grpc.WithBlock(),
|
||||
// grpc.WithBlock(),
|
||||
grpc.WithContextDialer(func(c context.Context, s string) (net.Conn, error) {
|
||||
return d.dial(ctx, "tcp", s, &options)
|
||||
}),
|
||||
@@ -82,6 +82,7 @@ func (d *grpcDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
|
||||
Backoff: backoff.DefaultConfig,
|
||||
MinConnectTimeout: 10 * time.Second,
|
||||
}),
|
||||
grpc.FailOnNonTempDialError(true),
|
||||
}
|
||||
if !d.md.insecure {
|
||||
grpcOpts = append(grpcOpts, grpc.WithTransportCredentials(credentials.NewTLS(d.options.TLSConfig)))
|
||||
@@ -91,7 +92,7 @@ func (d *grpcDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
|
||||
|
||||
cc, err := grpc.DialContext(ctx, addr, grpcOpts...)
|
||||
if err != nil {
|
||||
d.logger.Error(err)
|
||||
d.options.Logger.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
client = pb.NewGostTunelClient(cc)
|
||||
@@ -116,9 +117,9 @@ func (d *grpcDialer) dial(ctx context.Context, network, addr string, opts *diale
|
||||
if dial != nil {
|
||||
conn, err := dial(ctx, addr)
|
||||
if err != nil {
|
||||
d.logger.Error(err)
|
||||
d.options.Logger.Error(err)
|
||||
} else {
|
||||
d.logger.WithFields(map[string]interface{}{
|
||||
d.options.Logger.WithFields(map[string]interface{}{
|
||||
"src": conn.LocalAddr().String(),
|
||||
"dst": addr,
|
||||
}).Debug("dial with dial func")
|
||||
@@ -129,9 +130,9 @@ func (d *grpcDialer) dial(ctx context.Context, network, addr string, opts *diale
|
||||
var netd net.Dialer
|
||||
conn, err := netd.DialContext(ctx, network, addr)
|
||||
if err != nil {
|
||||
d.logger.Error(err)
|
||||
d.options.Logger.Error(err)
|
||||
} else {
|
||||
d.logger.WithFields(map[string]interface{}{
|
||||
d.options.Logger.WithFields(map[string]interface{}{
|
||||
"src": conn.LocalAddr().String(),
|
||||
"dst": addr,
|
||||
}).Debugf("dial direct %s/%s", addr, network)
|
||||
|
||||
@@ -10,16 +10,16 @@ import (
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
User *url.Userinfo
|
||||
Auth *url.Userinfo
|
||||
TLSConfig *tls.Config
|
||||
Logger logger.Logger
|
||||
}
|
||||
|
||||
type Option func(opts *Options)
|
||||
|
||||
func UserOption(user *url.Userinfo) Option {
|
||||
func AuthOption(auth *url.Userinfo) Option {
|
||||
return func(opts *Options) {
|
||||
opts.User = user
|
||||
opts.Auth = auth
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -20,7 +19,6 @@ func init() {
|
||||
}
|
||||
|
||||
type sshdDialer struct {
|
||||
user *url.Userinfo
|
||||
sessions map[string]*sshSession
|
||||
sessionMutex sync.Mutex
|
||||
md metadata
|
||||
@@ -34,7 +32,6 @@ func NewDialer(opts ...dialer.Option) dialer.Dialer {
|
||||
}
|
||||
|
||||
return &sshdDialer{
|
||||
user: options.User,
|
||||
sessions: make(map[string]*sshSession),
|
||||
options: options,
|
||||
}
|
||||
@@ -167,9 +164,9 @@ func (d *sshdDialer) initSession(ctx context.Context, addr string, conn net.Conn
|
||||
// Timeout: timeout,
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
}
|
||||
if d.user != nil {
|
||||
config.User = d.user.Username()
|
||||
if password, _ := d.user.Password(); password != "" {
|
||||
if d.options.Auth != nil {
|
||||
config.User = d.options.Auth.Username()
|
||||
if password, _ := d.options.Auth.Password(); password != "" {
|
||||
config.Auth = []ssh.AuthMethod{
|
||||
ssh.Password(password),
|
||||
}
|
||||
|
||||
@@ -16,9 +16,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/asaskevich/govalidator"
|
||||
"github.com/go-gost/gost/pkg/auth"
|
||||
"github.com/go-gost/gost/pkg/chain"
|
||||
auth_util "github.com/go-gost/gost/pkg/common/util/auth"
|
||||
"github.com/go-gost/gost/pkg/handler"
|
||||
"github.com/go-gost/gost/pkg/logger"
|
||||
md "github.com/go-gost/gost/pkg/metadata"
|
||||
@@ -30,10 +28,9 @@ func init() {
|
||||
}
|
||||
|
||||
type httpHandler struct {
|
||||
router *chain.Router
|
||||
authenticator auth.Authenticator
|
||||
md metadata
|
||||
options handler.Options
|
||||
router *chain.Router
|
||||
md metadata
|
||||
options handler.Options
|
||||
}
|
||||
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
@@ -52,7 +49,6 @@ func (h *httpHandler) Init(md md.Metadata) error {
|
||||
return err
|
||||
}
|
||||
|
||||
h.authenticator = auth_util.AuthFromUsers(h.options.Auths...)
|
||||
h.router = &chain.Router{
|
||||
Retries: h.options.Retries,
|
||||
Chain: h.options.Chain,
|
||||
@@ -266,7 +262,7 @@ func (h *httpHandler) basicProxyAuth(proxyAuth string, log logger.Logger) (usern
|
||||
|
||||
func (h *httpHandler) authenticate(conn net.Conn, req *http.Request, resp *http.Response, log logger.Logger) (ok bool) {
|
||||
u, p, _ := h.basicProxyAuth(req.Header.Get("Proxy-Authorization"), log)
|
||||
if h.authenticator == nil || h.authenticator.Authenticate(u, p) {
|
||||
if h.options.Auther == nil || h.options.Auther.Authenticate(u, p) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -18,9 +18,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/gost/pkg/auth"
|
||||
"github.com/go-gost/gost/pkg/chain"
|
||||
auth_util "github.com/go-gost/gost/pkg/common/util/auth"
|
||||
"github.com/go-gost/gost/pkg/handler"
|
||||
http2_util "github.com/go-gost/gost/pkg/internal/util/http2"
|
||||
"github.com/go-gost/gost/pkg/logger"
|
||||
@@ -33,10 +31,9 @@ func init() {
|
||||
}
|
||||
|
||||
type http2Handler struct {
|
||||
router *chain.Router
|
||||
authenticator auth.Authenticator
|
||||
md metadata
|
||||
options handler.Options
|
||||
router *chain.Router
|
||||
md metadata
|
||||
options handler.Options
|
||||
}
|
||||
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
@@ -55,7 +52,6 @@ func (h *http2Handler) Init(md md.Metadata) error {
|
||||
return err
|
||||
}
|
||||
|
||||
h.authenticator = auth_util.AuthFromUsers(h.options.Auths...)
|
||||
h.router = &chain.Router{
|
||||
Retries: h.options.Retries,
|
||||
Chain: h.options.Chain,
|
||||
@@ -239,7 +235,7 @@ func (h *http2Handler) basicProxyAuth(proxyAuth string) (username, password stri
|
||||
|
||||
func (h *http2Handler) authenticate(w http.ResponseWriter, r *http.Request, resp *http.Response, log logger.Logger) (ok bool) {
|
||||
u, p, _ := h.basicProxyAuth(r.Header.Get("Proxy-Authorization"))
|
||||
if h.authenticator == nil || h.authenticator.Authenticate(u, p) {
|
||||
if h.options.Auther == nil || h.options.Auther.Authenticate(u, p) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
+12
-5
@@ -4,6 +4,7 @@ import (
|
||||
"crypto/tls"
|
||||
"net/url"
|
||||
|
||||
"github.com/go-gost/gost/pkg/auth"
|
||||
"github.com/go-gost/gost/pkg/bypass"
|
||||
"github.com/go-gost/gost/pkg/chain"
|
||||
"github.com/go-gost/gost/pkg/hosts"
|
||||
@@ -13,11 +14,12 @@ import (
|
||||
|
||||
type Options struct {
|
||||
Retries int
|
||||
Chain *chain.Chain
|
||||
Chain chain.Chainer
|
||||
Resolver resolver.Resolver
|
||||
Hosts hosts.HostMapper
|
||||
Bypass bypass.Bypass
|
||||
Auths []*url.Userinfo
|
||||
Auth *url.Userinfo
|
||||
Auther auth.Authenticator
|
||||
TLSConfig *tls.Config
|
||||
Logger logger.Logger
|
||||
}
|
||||
@@ -30,7 +32,7 @@ func RetriesOption(retries int) Option {
|
||||
}
|
||||
}
|
||||
|
||||
func ChainOption(chain *chain.Chain) Option {
|
||||
func ChainOption(chain chain.Chainer) Option {
|
||||
return func(opts *Options) {
|
||||
opts.Chain = chain
|
||||
}
|
||||
@@ -54,9 +56,14 @@ func BypassOption(bypass bypass.Bypass) Option {
|
||||
}
|
||||
}
|
||||
|
||||
func AuthsOption(auths ...*url.Userinfo) Option {
|
||||
func AuthOption(auth *url.Userinfo) Option {
|
||||
return func(opts *Options) {
|
||||
opts.Auths = auths
|
||||
opts.Auth = auth
|
||||
}
|
||||
}
|
||||
func AutherOption(auther auth.Authenticator) Option {
|
||||
return func(opts *Options) {
|
||||
opts.Auther = auther
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,7 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/gost/pkg/auth"
|
||||
"github.com/go-gost/gost/pkg/chain"
|
||||
auth_util "github.com/go-gost/gost/pkg/common/util/auth"
|
||||
"github.com/go-gost/gost/pkg/handler"
|
||||
md "github.com/go-gost/gost/pkg/metadata"
|
||||
"github.com/go-gost/gost/pkg/registry"
|
||||
@@ -20,11 +18,10 @@ func init() {
|
||||
}
|
||||
|
||||
type relayHandler struct {
|
||||
group *chain.NodeGroup
|
||||
router *chain.Router
|
||||
authenticator auth.Authenticator
|
||||
md metadata
|
||||
options handler.Options
|
||||
group *chain.NodeGroup
|
||||
router *chain.Router
|
||||
md metadata
|
||||
options handler.Options
|
||||
}
|
||||
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
@@ -43,7 +40,6 @@ func (h *relayHandler) Init(md md.Metadata) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
h.authenticator = auth_util.AuthFromUsers(h.options.Auths...)
|
||||
h.router = &chain.Router{
|
||||
Retries: h.options.Retries,
|
||||
Chain: h.options.Chain,
|
||||
@@ -113,7 +109,7 @@ func (h *relayHandler) Handle(ctx context.Context, conn net.Conn) {
|
||||
Version: relay.Version1,
|
||||
Status: relay.StatusOK,
|
||||
}
|
||||
if h.authenticator != nil && !h.authenticator.Authenticate(user, pass) {
|
||||
if h.options.Auther != nil && !h.options.Auther.Authenticate(user, pass) {
|
||||
resp.Status = relay.StatusUnauthorized
|
||||
resp.WriteTo(conn)
|
||||
log.Error("unauthorized")
|
||||
|
||||
@@ -6,9 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/gosocks4"
|
||||
"github.com/go-gost/gost/pkg/auth"
|
||||
"github.com/go-gost/gost/pkg/chain"
|
||||
auth_util "github.com/go-gost/gost/pkg/common/util/auth"
|
||||
"github.com/go-gost/gost/pkg/handler"
|
||||
"github.com/go-gost/gost/pkg/logger"
|
||||
md "github.com/go-gost/gost/pkg/metadata"
|
||||
@@ -21,10 +19,9 @@ func init() {
|
||||
}
|
||||
|
||||
type socks4Handler struct {
|
||||
router *chain.Router
|
||||
authenticator auth.Authenticator
|
||||
md metadata
|
||||
options handler.Options
|
||||
router *chain.Router
|
||||
md metadata
|
||||
options handler.Options
|
||||
}
|
||||
|
||||
func NewHandler(opts ...handler.Option) handler.Handler {
|
||||
@@ -43,7 +40,6 @@ func (h *socks4Handler) Init(md md.Metadata) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
h.authenticator = auth_util.AuthFromUsers(h.options.Auths...)
|
||||
h.router = &chain.Router{
|
||||
Retries: h.options.Retries,
|
||||
Chain: h.options.Chain,
|
||||
@@ -85,8 +81,8 @@ func (h *socks4Handler) Handle(ctx context.Context, conn net.Conn) {
|
||||
|
||||
conn.SetReadDeadline(time.Time{})
|
||||
|
||||
if h.authenticator != nil &&
|
||||
!h.authenticator.Authenticate(string(req.Userid), "") {
|
||||
if h.options.Auther != nil &&
|
||||
!h.options.Auther.Authenticate(string(req.Userid), "") {
|
||||
resp := gosocks4.NewReply(gosocks4.RejectedUserid, nil)
|
||||
resp.Write(conn)
|
||||
log.Debug(resp)
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/go-gost/gosocks5"
|
||||
"github.com/go-gost/gost/pkg/chain"
|
||||
auth_util "github.com/go-gost/gost/pkg/common/util/auth"
|
||||
"github.com/go-gost/gost/pkg/common/util/socks"
|
||||
"github.com/go-gost/gost/pkg/handler"
|
||||
md "github.com/go-gost/gost/pkg/metadata"
|
||||
@@ -51,7 +50,7 @@ func (h *socks5Handler) Init(md md.Metadata) (err error) {
|
||||
}
|
||||
|
||||
h.selector = &serverSelector{
|
||||
Authenticator: auth_util.AuthFromUsers(h.options.Auths...),
|
||||
Authenticator: h.options.Auther,
|
||||
TLSConfig: h.options.TLSConfig,
|
||||
logger: h.options.Logger,
|
||||
noTLS: h.md.noTLS,
|
||||
|
||||
@@ -42,9 +42,9 @@ func (h *ssHandler) Init(md md.Metadata) (err error) {
|
||||
if err = h.parseMetadata(md); err != nil {
|
||||
return
|
||||
}
|
||||
if len(h.options.Auths) > 0 {
|
||||
method := h.options.Auths[0].Username()
|
||||
password, _ := h.options.Auths[0].Password()
|
||||
if h.options.Auth != nil {
|
||||
method := h.options.Auth.Username()
|
||||
password, _ := h.options.Auth.Password()
|
||||
h.cipher, err = ss.ShadowCipher(method, password, h.md.key)
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
@@ -43,9 +43,9 @@ func (h *ssuHandler) Init(md md.Metadata) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
if len(h.options.Auths) > 0 {
|
||||
method := h.options.Auths[0].Username()
|
||||
password, _ := h.options.Auths[0].Password()
|
||||
if h.options.Auth != nil {
|
||||
method := h.options.Auth.Username()
|
||||
password, _ := h.options.Auth.Password()
|
||||
h.cipher, err = ss.ShadowCipher(method, password, h.md.key)
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
@@ -54,9 +54,9 @@ func (h *tapHandler) Init(md md.Metadata) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
if len(h.options.Auths) > 0 {
|
||||
method := h.options.Auths[0].Username()
|
||||
password, _ := h.options.Auths[0].Password()
|
||||
if h.options.Auth != nil {
|
||||
method := h.options.Auth.Username()
|
||||
password, _ := h.options.Auth.Password()
|
||||
h.cipher, err = ss.ShadowCipher(method, password, h.md.key)
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
@@ -56,9 +56,9 @@ func (h *tunHandler) Init(md md.Metadata) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
if len(h.options.Auths) > 0 {
|
||||
method := h.options.Auths[0].Username()
|
||||
password, _ := h.options.Auths[0].Password()
|
||||
if h.options.Auth != nil {
|
||||
method := h.options.Auth.Username()
|
||||
password, _ := h.options.Auth.Password()
|
||||
h.cipher, err = ss.ShadowCipher(method, password, h.md.key)
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
+33
-10
@@ -2,6 +2,7 @@ package hosts
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/go-gost/gost/pkg/logger"
|
||||
@@ -61,24 +62,33 @@ func (h *Hosts) Map(ip net.IP, hostname string, aliases ...string) {
|
||||
|
||||
// Lookup searches the IP address corresponds to the given network and host from the host table.
|
||||
// The network should be 'ip', 'ip4' or 'ip6', default network is 'ip'.
|
||||
// the host should be a hostname (example.org) or a hostname with dot prefix (.example.org).
|
||||
func (h *Hosts) Lookup(network, host string) (ips []net.IP, ok bool) {
|
||||
if h == nil || host == "" {
|
||||
return
|
||||
m := h.lookup(host)
|
||||
if m == nil {
|
||||
m = h.lookup("." + host)
|
||||
}
|
||||
if m == nil {
|
||||
s := host
|
||||
for {
|
||||
if index := strings.IndexByte(s, '.'); index > 0 {
|
||||
m = h.lookup(s[index:])
|
||||
s = s[index+1:]
|
||||
if m == nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
v, ok := h.mappings.Load(host)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
m, _ := v.(*hostMapping)
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// hostname alias
|
||||
if host != m.Hostname {
|
||||
v, _ = h.mappings.Load(m.Hostname)
|
||||
m, _ = v.(*hostMapping)
|
||||
if !strings.HasPrefix(m.Hostname, ".") && host != m.Hostname {
|
||||
m = h.lookup(m.Hostname)
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
@@ -107,3 +117,16 @@ func (h *Hosts) Lookup(network, host string) (ips []net.IP, ok bool) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (h *Hosts) lookup(host string) *hostMapping {
|
||||
if h == nil || host == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
v, ok := h.mappings.Load(host)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
m, _ := v.(*hostMapping)
|
||||
return m
|
||||
}
|
||||
|
||||
@@ -180,6 +180,9 @@ func (l *dnsListener) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/dns-message")
|
||||
|
||||
raddr, _ := net.ResolveTCPAddr("tcp", r.RemoteAddr)
|
||||
if raddr == nil {
|
||||
raddr = &net.TCPAddr{}
|
||||
}
|
||||
if err := l.serve(&dohResponseWriter{raddr: raddr, ResponseWriter: w}, buf); err != nil {
|
||||
l.logger.Error(err)
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
pb "github.com/go-gost/gost/pkg/common/util/grpc/proto"
|
||||
"github.com/go-gost/gost/pkg/logger"
|
||||
"google.golang.org/grpc/peer"
|
||||
)
|
||||
|
||||
type server struct {
|
||||
@@ -24,6 +25,9 @@ func (s *server) Tunnel(srv pb.GostTunel_TunnelServer) error {
|
||||
remoteAddr: &net.TCPAddr{},
|
||||
closed: make(chan struct{}),
|
||||
}
|
||||
if p, ok := peer.FromContext(srv.Context()); ok {
|
||||
c.remoteAddr = p.Addr
|
||||
}
|
||||
|
||||
select {
|
||||
case s.cqueue <- c:
|
||||
|
||||
+13
-5
@@ -4,15 +4,17 @@ import (
|
||||
"crypto/tls"
|
||||
"net/url"
|
||||
|
||||
"github.com/go-gost/gost/pkg/auth"
|
||||
"github.com/go-gost/gost/pkg/chain"
|
||||
"github.com/go-gost/gost/pkg/logger"
|
||||
)
|
||||
|
||||
type Options struct {
|
||||
Addr string
|
||||
Auths []*url.Userinfo
|
||||
Auther auth.Authenticator
|
||||
Auth *url.Userinfo
|
||||
TLSConfig *tls.Config
|
||||
Chain *chain.Chain
|
||||
Chain chain.Chainer
|
||||
Logger logger.Logger
|
||||
}
|
||||
|
||||
@@ -24,9 +26,15 @@ func AddrOption(addr string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
func AuthsOption(auths ...*url.Userinfo) Option {
|
||||
func AutherOption(auther auth.Authenticator) Option {
|
||||
return func(opts *Options) {
|
||||
opts.Auths = auths
|
||||
opts.Auther = auther
|
||||
}
|
||||
}
|
||||
|
||||
func AuthOption(auth *url.Userinfo) Option {
|
||||
return func(opts *Options) {
|
||||
opts.Auth = auth
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +44,7 @@ func TLSConfigOption(tlsConfig *tls.Config) Option {
|
||||
}
|
||||
}
|
||||
|
||||
func ChainOption(chain *chain.Chain) Option {
|
||||
func ChainOption(chain chain.Chainer) Option {
|
||||
return func(opts *Options) {
|
||||
opts.Chain = chain
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
auth_util "github.com/go-gost/gost/pkg/common/util/auth"
|
||||
ssh_util "github.com/go-gost/gost/pkg/internal/util/ssh"
|
||||
"github.com/go-gost/gost/pkg/listener"
|
||||
"github.com/go-gost/gost/pkg/logger"
|
||||
@@ -53,13 +52,12 @@ func (l *sshListener) Init(md md.Metadata) (err error) {
|
||||
|
||||
l.Listener = ln
|
||||
|
||||
authenticator := auth_util.AuthFromUsers(l.options.Auths...)
|
||||
config := &ssh.ServerConfig{
|
||||
PasswordCallback: ssh_util.PasswordCallback(authenticator),
|
||||
PasswordCallback: ssh_util.PasswordCallback(l.options.Auther),
|
||||
PublicKeyCallback: ssh_util.PublicKeyCallback(l.md.authorizedKeys),
|
||||
}
|
||||
config.AddHostKey(l.md.signer)
|
||||
if authenticator == nil && len(l.md.authorizedKeys) == 0 {
|
||||
if l.options.Auther == nil && len(l.md.authorizedKeys) == 0 {
|
||||
config.NoClientAuth = true
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
auth_util "github.com/go-gost/gost/pkg/common/util/auth"
|
||||
ssh_util "github.com/go-gost/gost/pkg/internal/util/ssh"
|
||||
sshd_util "github.com/go-gost/gost/pkg/internal/util/sshd"
|
||||
"github.com/go-gost/gost/pkg/listener"
|
||||
@@ -62,13 +61,12 @@ func (l *sshdListener) Init(md md.Metadata) (err error) {
|
||||
|
||||
l.Listener = ln
|
||||
|
||||
authenticator := auth_util.AuthFromUsers(l.options.Auths...)
|
||||
config := &ssh.ServerConfig{
|
||||
PasswordCallback: ssh_util.PasswordCallback(authenticator),
|
||||
PasswordCallback: ssh_util.PasswordCallback(l.options.Auther),
|
||||
PublicKeyCallback: ssh_util.PublicKeyCallback(l.md.authorizedKeys),
|
||||
}
|
||||
config.AddHostKey(l.md.signer)
|
||||
if authenticator == nil && len(l.md.authorizedKeys) == 0 {
|
||||
if l.options.Auther == nil && len(l.md.authorizedKeys) == 0 {
|
||||
config.NoClientAuth = true
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,10 @@ func Default() Logger {
|
||||
return defaultLogger
|
||||
}
|
||||
|
||||
func SetDefault(logger Logger) {
|
||||
defaultLogger = logger
|
||||
}
|
||||
|
||||
type logger struct {
|
||||
logger *logrus.Entry
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/go-gost/gost/pkg/admission"
|
||||
)
|
||||
|
||||
var (
|
||||
admissionReg = &admissionRegistry{}
|
||||
)
|
||||
|
||||
func Admission() *admissionRegistry {
|
||||
return admissionReg
|
||||
}
|
||||
|
||||
type admissionRegistry struct {
|
||||
m sync.Map
|
||||
}
|
||||
|
||||
func (r *admissionRegistry) Register(name string, admission admission.Admission) error {
|
||||
if name == "" || admission == nil {
|
||||
return nil
|
||||
}
|
||||
if _, loaded := r.m.LoadOrStore(name, admission); loaded {
|
||||
return ErrDup
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *admissionRegistry) Unregister(name string) {
|
||||
r.m.Delete(name)
|
||||
}
|
||||
|
||||
func (r *admissionRegistry) IsRegistered(name string) bool {
|
||||
_, ok := r.m.Load(name)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (r *admissionRegistry) Get(name string) admission.Admission {
|
||||
if name == "" {
|
||||
return nil
|
||||
}
|
||||
return &admissionWrapper{name: name}
|
||||
}
|
||||
|
||||
func (r *admissionRegistry) get(name string) admission.Admission {
|
||||
if v, ok := r.m.Load(name); ok {
|
||||
return v.(admission.Admission)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type admissionWrapper struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (w *admissionWrapper) Admit(addr string) bool {
|
||||
p := admissionReg.get(w.name)
|
||||
if p == nil {
|
||||
return false
|
||||
}
|
||||
return p.Admit(addr)
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/go-gost/gost/pkg/auth"
|
||||
)
|
||||
|
||||
var (
|
||||
autherReg = &autherRegistry{}
|
||||
)
|
||||
|
||||
func Auther() *autherRegistry {
|
||||
return autherReg
|
||||
}
|
||||
|
||||
type autherRegistry struct {
|
||||
m sync.Map
|
||||
}
|
||||
|
||||
func (r *autherRegistry) Register(name string, auther auth.Authenticator) error {
|
||||
if name == "" || auther == nil {
|
||||
return nil
|
||||
}
|
||||
if _, loaded := r.m.LoadOrStore(name, auther); loaded {
|
||||
return ErrDup
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *autherRegistry) Unregister(name string) {
|
||||
r.m.Delete(name)
|
||||
}
|
||||
|
||||
func (r *autherRegistry) IsRegistered(name string) bool {
|
||||
_, ok := r.m.Load(name)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (r *autherRegistry) Get(name string) auth.Authenticator {
|
||||
if name == "" {
|
||||
return nil
|
||||
}
|
||||
return &autherWrapper{name: name}
|
||||
}
|
||||
|
||||
func (r *autherRegistry) get(name string) auth.Authenticator {
|
||||
if v, ok := r.m.Load(name); ok {
|
||||
return v.(auth.Authenticator)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type autherWrapper struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (w *autherWrapper) Authenticate(user, password string) bool {
|
||||
v := autherReg.get(w.name)
|
||||
if v == nil {
|
||||
return true
|
||||
}
|
||||
return v.Authenticate(user, password)
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/go-gost/gost/pkg/bypass"
|
||||
)
|
||||
|
||||
var (
|
||||
bypassReg = &bypassRegistry{}
|
||||
)
|
||||
|
||||
func Bypass() *bypassRegistry {
|
||||
return bypassReg
|
||||
}
|
||||
|
||||
type bypassRegistry struct {
|
||||
m sync.Map
|
||||
}
|
||||
|
||||
func (r *bypassRegistry) Register(name string, bypass bypass.Bypass) error {
|
||||
if name == "" || bypass == nil {
|
||||
return nil
|
||||
}
|
||||
if _, loaded := r.m.LoadOrStore(name, bypass); loaded {
|
||||
return ErrDup
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *bypassRegistry) Unregister(name string) {
|
||||
r.m.Delete(name)
|
||||
}
|
||||
|
||||
func (r *bypassRegistry) IsRegistered(name string) bool {
|
||||
_, ok := r.m.Load(name)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (r *bypassRegistry) Get(name string) bypass.Bypass {
|
||||
if name == "" {
|
||||
return nil
|
||||
}
|
||||
return &bypassWrapper{name: name}
|
||||
}
|
||||
|
||||
func (r *bypassRegistry) get(name string) bypass.Bypass {
|
||||
if v, ok := r.m.Load(name); ok {
|
||||
return v.(bypass.Bypass)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type bypassWrapper struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (w *bypassWrapper) Contains(addr string) bool {
|
||||
bp := bypassReg.get(w.name)
|
||||
if bp == nil {
|
||||
return false
|
||||
}
|
||||
return bp.Contains(addr)
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/go-gost/gost/pkg/chain"
|
||||
)
|
||||
|
||||
var (
|
||||
chainReg = &chainRegistry{}
|
||||
)
|
||||
|
||||
func Chain() *chainRegistry {
|
||||
return chainReg
|
||||
}
|
||||
|
||||
type chainRegistry struct {
|
||||
m sync.Map
|
||||
}
|
||||
|
||||
func (r *chainRegistry) Register(name string, chain chain.Chainer) error {
|
||||
if name == "" || chain == nil {
|
||||
return nil
|
||||
}
|
||||
if _, loaded := r.m.LoadOrStore(name, chain); loaded {
|
||||
return ErrDup
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *chainRegistry) Unregister(name string) {
|
||||
r.m.Delete(name)
|
||||
}
|
||||
|
||||
func (r *chainRegistry) IsRegistered(name string) bool {
|
||||
_, ok := r.m.Load(name)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (r *chainRegistry) Get(name string) chain.Chainer {
|
||||
if name == "" {
|
||||
return nil
|
||||
}
|
||||
return &chainWrapper{name: name}
|
||||
}
|
||||
|
||||
func (r *chainRegistry) get(name string) chain.Chainer {
|
||||
if v, ok := r.m.Load(name); ok {
|
||||
return v.(chain.Chainer)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type chainWrapper struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (w *chainWrapper) Route(network, address string) *chain.Route {
|
||||
v := Chain().get(w.name)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
return v.Route(network, address)
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/go-gost/gost/pkg/hosts"
|
||||
)
|
||||
|
||||
var (
|
||||
hostsReg = &hostsRegistry{}
|
||||
)
|
||||
|
||||
func Hosts() *hostsRegistry {
|
||||
return hostsReg
|
||||
}
|
||||
|
||||
type hostsRegistry struct {
|
||||
m sync.Map
|
||||
}
|
||||
|
||||
func (r *hostsRegistry) Register(name string, hosts hosts.HostMapper) error {
|
||||
if name == "" || hosts == nil {
|
||||
return nil
|
||||
}
|
||||
if _, loaded := r.m.LoadOrStore(name, hosts); loaded {
|
||||
return ErrDup
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *hostsRegistry) Unregister(name string) {
|
||||
r.m.Delete(name)
|
||||
}
|
||||
|
||||
func (r *hostsRegistry) IsRegistered(name string) bool {
|
||||
_, ok := r.m.Load(name)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (r *hostsRegistry) Get(name string) hosts.HostMapper {
|
||||
if name == "" {
|
||||
return nil
|
||||
}
|
||||
return &hostsWrapper{name: name}
|
||||
}
|
||||
|
||||
func (r *hostsRegistry) get(name string) hosts.HostMapper {
|
||||
if v, ok := r.m.Load(name); ok {
|
||||
return v.(hosts.HostMapper)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type hostsWrapper struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (w *hostsWrapper) Lookup(network, host string) ([]net.IP, bool) {
|
||||
v := Hosts().get(w.name)
|
||||
if v == nil {
|
||||
return nil, false
|
||||
}
|
||||
return v.Lookup(network, host)
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/go-gost/gost/pkg/connector"
|
||||
"github.com/go-gost/gost/pkg/dialer"
|
||||
"github.com/go-gost/gost/pkg/handler"
|
||||
@@ -8,6 +10,10 @@ import (
|
||||
"github.com/go-gost/gost/pkg/logger"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrDup = errors.New("registry: duplicate instance")
|
||||
)
|
||||
|
||||
type NewListener func(opts ...listener.Option) listener.Listener
|
||||
type NewHandler func(opts ...handler.Option) handler.Handler
|
||||
type NewDialer func(opts ...dialer.Option) dialer.Dialer
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/go-gost/gost/pkg/resolver"
|
||||
)
|
||||
|
||||
var (
|
||||
resolverReg = &resolverRegistry{}
|
||||
)
|
||||
|
||||
func Resolver() *resolverRegistry {
|
||||
return resolverReg
|
||||
}
|
||||
|
||||
type resolverRegistry struct {
|
||||
m sync.Map
|
||||
}
|
||||
|
||||
func (r *resolverRegistry) Register(name string, resolver resolver.Resolver) error {
|
||||
if name == "" || resolver == nil {
|
||||
return nil
|
||||
}
|
||||
if _, loaded := r.m.LoadOrStore(name, resolver); loaded {
|
||||
return ErrDup
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *resolverRegistry) Unregister(name string) {
|
||||
r.m.Delete(name)
|
||||
}
|
||||
|
||||
func (r *resolverRegistry) IsRegistered(name string) bool {
|
||||
_, ok := r.m.Load(name)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (r *resolverRegistry) Get(name string) resolver.Resolver {
|
||||
if name == "" {
|
||||
return nil
|
||||
}
|
||||
return &resolverWrapper{name: name}
|
||||
}
|
||||
|
||||
func (r *resolverRegistry) get(name string) resolver.Resolver {
|
||||
if v, ok := r.m.Load(name); ok {
|
||||
return v.(resolver.Resolver)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type resolverWrapper struct {
|
||||
name string
|
||||
}
|
||||
|
||||
func (w *resolverWrapper) Resolve(ctx context.Context, network, host string) ([]net.IP, error) {
|
||||
r := Resolver().get(w.name)
|
||||
if r == nil {
|
||||
return nil, resolver.ErrInvalid
|
||||
}
|
||||
return r.Resolve(ctx, network, host)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package registry
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/go-gost/gost/pkg/service"
|
||||
)
|
||||
|
||||
var (
|
||||
svcReg = &serviceRegistry{}
|
||||
)
|
||||
|
||||
func Service() *serviceRegistry {
|
||||
return svcReg
|
||||
}
|
||||
|
||||
type serviceRegistry struct {
|
||||
m sync.Map
|
||||
}
|
||||
|
||||
func (r *serviceRegistry) Register(name string, svc service.Service) error {
|
||||
if name == "" || svc == nil {
|
||||
return nil
|
||||
}
|
||||
if _, loaded := r.m.LoadOrStore(name, svc); loaded {
|
||||
return ErrDup
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *serviceRegistry) Unregister(name string) {
|
||||
r.m.Delete(name)
|
||||
}
|
||||
|
||||
func (r *serviceRegistry) IsRegistered(name string) bool {
|
||||
_, ok := r.m.Load(name)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (r *serviceRegistry) Get(name string) service.Service {
|
||||
if name == "" {
|
||||
return nil
|
||||
}
|
||||
v, ok := r.m.Load(name)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return v.(service.Service)
|
||||
}
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
|
||||
type NameServer struct {
|
||||
Addr string
|
||||
Chain *chain.Chain
|
||||
Chain chain.Chainer
|
||||
TTL time.Duration
|
||||
Timeout time.Duration
|
||||
ClientIP net.IP
|
||||
@@ -89,7 +89,7 @@ func NewResolver(nameservers []NameServer, opts ...ResolverOption) (resolverpkg.
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *resolver) Resolve(ctx context.Context, host string) (ips []net.IP, err error) {
|
||||
func (r *resolver) Resolve(ctx context.Context, network, host string) (ips []net.IP, err error) {
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
return []net.IP{ip}, nil
|
||||
}
|
||||
|
||||
@@ -2,10 +2,16 @@ package resolver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalid = errors.New("resolver invalid")
|
||||
)
|
||||
|
||||
type Resolver interface {
|
||||
// Resolve returns a slice of the host's IPv4 and IPv6 addresses.
|
||||
Resolve(ctx context.Context, host string) ([]net.IP, error)
|
||||
// The network should be 'ip', 'ip4' or 'ip6', default network is 'ip'.
|
||||
Resolve(ctx context.Context, network, host string) ([]net.IP, error)
|
||||
}
|
||||
|
||||
+50
-24
@@ -5,45 +5,64 @@ import (
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/go-gost/gost/pkg/admission"
|
||||
"github.com/go-gost/gost/pkg/handler"
|
||||
"github.com/go-gost/gost/pkg/listener"
|
||||
"github.com/go-gost/gost/pkg/logger"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
type options struct {
|
||||
admission admission.Admission
|
||||
logger logger.Logger
|
||||
}
|
||||
|
||||
type Option func(opts *options)
|
||||
|
||||
func AdmissionOption(admission admission.Admission) Option {
|
||||
return func(opts *options) {
|
||||
opts.admission = admission
|
||||
}
|
||||
}
|
||||
|
||||
func LoggerOption(logger logger.Logger) Option {
|
||||
return func(opts *options) {
|
||||
opts.logger = logger
|
||||
}
|
||||
}
|
||||
|
||||
type Service interface {
|
||||
Serve() error
|
||||
Addr() net.Addr
|
||||
Close() error
|
||||
}
|
||||
|
||||
type service struct {
|
||||
listener listener.Listener
|
||||
handler handler.Handler
|
||||
logger logger.Logger
|
||||
options options
|
||||
}
|
||||
|
||||
func (s *Service) WithListener(ln listener.Listener) *Service {
|
||||
s.listener = ln
|
||||
return s
|
||||
func NewService(ln listener.Listener, h handler.Handler, opts ...Option) Service {
|
||||
var options options
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
return &service{
|
||||
listener: ln,
|
||||
handler: h,
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) WithHandler(h handler.Handler) *Service {
|
||||
s.handler = h
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Service) WithLogger(logger logger.Logger) *Service {
|
||||
s.logger = logger
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *Service) Addr() net.Addr {
|
||||
func (s *service) Addr() net.Addr {
|
||||
return s.listener.Addr()
|
||||
}
|
||||
|
||||
func (s *Service) Run() error {
|
||||
return s.serve()
|
||||
}
|
||||
|
||||
func (s *Service) Close() error {
|
||||
func (s *service) Close() error {
|
||||
return s.listener.Close()
|
||||
}
|
||||
|
||||
func (s *Service) serve() error {
|
||||
func (s *service) Serve() error {
|
||||
var tempDelay time.Duration
|
||||
for {
|
||||
conn, e := s.listener.Accept()
|
||||
@@ -57,15 +76,22 @@ func (s *Service) serve() error {
|
||||
if max := 5 * time.Second; tempDelay > max {
|
||||
tempDelay = max
|
||||
}
|
||||
s.logger.Warnf("accept: %v, retrying in %v", e, tempDelay)
|
||||
s.options.logger.Warnf("accept: %v, retrying in %v", e, tempDelay)
|
||||
time.Sleep(tempDelay)
|
||||
continue
|
||||
}
|
||||
s.logger.Errorf("accept: %v", e)
|
||||
s.options.logger.Errorf("accept: %v", e)
|
||||
return e
|
||||
}
|
||||
tempDelay = 0
|
||||
|
||||
if s.options.admission != nil &&
|
||||
!s.options.admission.Admit(conn.RemoteAddr().String()) {
|
||||
s.options.logger.Infof("admission: %s is denied", conn.RemoteAddr())
|
||||
conn.Close()
|
||||
continue
|
||||
}
|
||||
|
||||
go s.handler.Handle(context.Background(), conn)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user