Compare commits

..
Author SHA1 Message Date
ginuerzh 4364190e6d fix icmp tunnel 2022-03-09 18:29:45 +08:00
ginuerzh be374b6488 add icmp tunnel 2022-03-08 22:34:39 +08:00
ginuerzh ee72cea036 add handler error metrics 2022-03-05 16:37:45 +08:00
ginuerzh e587b4df7c add metrics for service 2022-03-05 00:28:13 +08:00
ginuerzh 8d8785f534 add keepalive for websocket 2022-03-03 22:21:38 +08:00
ginuerzh b96d37d4cc dialer: add bind device 2022-03-02 21:36:27 +08:00
ginuerzh ffdf11e89e support multiple network interfaces 2022-03-01 21:48:50 +08:00
ginuerzh 07132d8de7 update registry 2022-02-27 22:32:15 +08:00
ginuerzh 0aee4f0ebd update for go1.18 2022-02-25 13:08:05 +08:00
ginuerzh 9db7137cd3 update Dockerfile 2022-02-23 22:25:52 +08:00
ginuerzh 307a90c20e add admission 2022-02-17 23:30:13 +08:00
ginuerzh 5daefb8e3c add basic auth for config webapi 2022-02-14 22:50:06 +08:00
ginuerzh edca3e0a55 fix issue#10 2022-02-13 20:40:37 +08:00
ginuerzh a8804ea02d fix auth for webapi 2022-02-12 21:32:32 +08:00
ginuerzh f2d806886a add basic auth for webapi 2022-02-12 21:05:39 +08:00
ginuerzh fdd67a6086 add auther api 2022-02-12 14:12:09 +08:00
ginuerzh 345338d410 hosts: support dot prefix mapping key 2022-02-12 14:11:59 +08:00
ginuerzh a8a6bbc3a3 add auther config 2022-02-12 00:33:20 +08:00
ginuerzh c1bf501734 update webapi 2022-02-09 11:37:09 +08:00
ginuerzh f1eaef8d69 add web api 2022-02-08 23:46:54 +08:00
ginuerzh 0fba6d2500 fix service parsing 2022-02-07 22:20:43 +08:00
ginuerzh 1ec10ff7dd update config parsing 2022-02-07 22:15:16 +08:00
ginuerzh 0983ecc52f fix tls config 2022-02-04 21:16:39 +08:00
180 changed files with 7228 additions and 2262 deletions
+1 -1
View File
@@ -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/s390x
# https://github.com/crazy-max/ghaction-docker-buildx
- name: Set up Docker Buildx
+1 -1
View File
@@ -1,4 +1,4 @@
FROM --platform=$BUILDPLATFORM golang:1-alpine as builder
FROM --platform=$BUILDPLATFORM golang:1.18rc1-alpine as builder
# Convert TARGETPLATFORM to GOARCH format
# https://github.com/tonistiigi/xx
+6 -2
View File
@@ -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)
+61 -18
View File
@@ -11,6 +11,7 @@ import (
"github.com/go-gost/gost/pkg/config"
"github.com/go-gost/gost/pkg/metadata"
metrics "github.com/go-gost/gost/pkg/metrics/service"
"github.com/go-gost/gost/pkg/registry"
)
@@ -34,8 +35,20 @@ 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.DefaultPath,
}
}
if v := os.Getenv("GOST_LOGGING_LEVEL"); v != "" {
cfg.Log = &config.LogConfig{
Level: v,
}
}
@@ -107,7 +120,7 @@ func buildConfigFromCmd(services, nodes stringList) (*config.Config, error) {
}
resolverCfg.Nameservers = append(
resolverCfg.Nameservers,
config.NameserverConfig{
&config.NameserverConfig{
Addr: rs,
},
)
@@ -127,7 +140,7 @@ func buildConfigFromCmd(services, nodes stringList) (*config.Config, error) {
}
hostsCfg.Mappings = append(
hostsCfg.Mappings,
config.HostMappingConfig{
&config.HostMappingConfig{
Hostname: ss[0],
IP: ss[1],
},
@@ -138,6 +151,11 @@ func buildConfigFromCmd(services, nodes stringList) (*config.Config, error) {
md.Del("hosts")
}
if v := metadata.GetString(md, "interface"); v != "" {
hopConfig.Interface = v
md.Del("interface")
}
chain.Hops = append(chain.Hops, hopConfig)
}
@@ -166,6 +184,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 +230,7 @@ func buildConfigFromCmd(services, nodes stringList) (*config.Config, error) {
}
resolverCfg.Nameservers = append(
resolverCfg.Nameservers,
config.NameserverConfig{
&config.NameserverConfig{
Addr: rs,
},
)
@@ -214,7 +250,7 @@ func buildConfigFromCmd(services, nodes stringList) (*config.Config, error) {
}
hostsCfg.Mappings = append(
hostsCfg.Mappings,
config.HostMappingConfig{
&config.HostMappingConfig{
Hostname: ss[0],
IP: ss[1],
},
@@ -245,10 +281,10 @@ func buildServiceConfig(url *url.URL) (*config.ServiceConfig, error) {
Addr: url.Host,
}
if h := registry.GetHandler(handler); h == nil {
if h := registry.HandlerRegistry().Get(handler); h == nil {
handler = "auto"
}
if ln := registry.GetListener(listener); ln == nil {
if ln := registry.ListenerRegistry().Get(listener); ln == nil {
listener = "tcp"
if handler == "ssu" {
listener = "udp"
@@ -271,13 +307,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 +327,7 @@ func buildServiceConfig(url *url.URL) (*config.ServiceConfig, error) {
if err != nil {
return nil, err
}
auths = append(auths, au)
auth = au
}
md.Del("auth")
@@ -312,6 +347,10 @@ func buildServiceConfig(url *url.URL) (*config.ServiceConfig, error) {
if v := metadata.GetString(md, "dns"); v != "" {
md.Set("dns", strings.Split(v, ","))
}
if v := metadata.GetString(md, "interface"); v != "" {
svc.Interface = v
md.Del("interface")
}
if svc.Forwarder != nil {
svc.Forwarder.Selector = parseSelector(md)
@@ -319,7 +358,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 +368,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
@@ -354,10 +393,10 @@ func buildNodeConfig(url *url.URL) (*config.NodeConfig, error) {
Addr: url.Host,
}
if c := registry.GetConnector(connector); c == nil {
if c := registry.ConnectorRegistry().Get(connector); c == nil {
connector = "http"
}
if d := registry.GetDialer(dialer); d == nil {
if d := registry.DialerRegistry().Get(dialer); d == nil {
dialer = "tcp"
if connector == "ssu" {
dialer = "udp"
@@ -389,6 +428,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 +437,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
}
+68 -363
View File
@@ -1,308 +1,95 @@
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"
metrics "github.com/go-gost/gost/pkg/metrics/service"
"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.AutherRegistry().Register(autherCfg.Name, auther); err != nil {
log.Fatal(err)
}
}
}
for _, admissionCfg := range cfg.Admissions {
if adm := parsing.ParseAdmission(admissionCfg); adm != nil {
if err := registry.AdmissionRegistry().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.BypassRegistry().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.ResolverRegistry().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.HostsRegistry().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.ChainRegistry().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.ServiceRegistry().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 +101,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 +120,22 @@ func logFromConfig(cfg *config.LogConfig) logger.Logger {
return logger.NewLogger(opts...)
}
func selectorFromConfig(cfg *config.SelectorConfig) chain.Selector {
if cfg == nil {
return nil
func buildAPIService(cfg *config.APIConfig) (service.Service, error) {
auther := parsing.ParseAutherFromAuth(cfg.Auth)
if cfg.Auther != "" {
auther = registry.AutherRegistry().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.NewService(
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 buildMetricsService(cfg *config.MetricsConfig) (service.Service, error) {
return metrics.NewService(
cfg.Addr,
metrics.PathOption(cfg.Path),
)
}
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
}
+58 -24
View File
@@ -3,7 +3,6 @@ package main
import (
"flag"
"fmt"
"io"
"net/http"
_ "net/http/pprof"
"os"
@@ -11,16 +10,19 @@ import (
"github.com/go-gost/gost/pkg/config"
"github.com/go-gost/gost/pkg/logger"
metrics "github.com/go-gost/gost/pkg/metrics/service"
)
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
metricsAddr string
)
func init() {
@@ -30,8 +32,10 @@ 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 service address")
flag.StringVar(&metricsAddr, "metrics", "", "metrics service address")
flag.Parse()
if printVersion {
@@ -44,7 +48,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 +56,17 @@ func main() {
}
cfg.Log.Level = string(logger.DebugLevel)
}
if apiAddr != "" {
cfg.API = &config.APIConfig{
Addr: apiAddr,
}
}
if metricsAddr != "" {
cfg.Metrics = &config.MetricsConfig{
Addr: metricsAddr,
Path: metrics.DefaultPath,
}
}
} else {
if cfgFile != "" {
err = cfg.ReadFile(cfgFile)
@@ -65,41 +80,60 @@ 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 := buildAPIService(cfg.API)
if err != nil {
log.Fatal(err)
}
defer s.Close()
go func() {
log.Info("api service on ", s.Addr())
log.Fatal(s.Serve())
}()
}
if cfg.Metrics != nil {
s, err := buildMetricsService(cfg.Metrics)
if err != nil {
log.Fatal(err)
}
defer s.Close()
go func() {
log.Info("metrics service 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 {}
}
+2
View File
@@ -19,6 +19,7 @@ import (
_ "github.com/go-gost/gost/pkg/dialer/http2"
_ "github.com/go-gost/gost/pkg/dialer/http2/h2"
_ "github.com/go-gost/gost/pkg/dialer/http3"
_ "github.com/go-gost/gost/pkg/dialer/icmp"
_ "github.com/go-gost/gost/pkg/dialer/kcp"
_ "github.com/go-gost/gost/pkg/dialer/obfs/http"
_ "github.com/go-gost/gost/pkg/dialer/obfs/tls"
@@ -58,6 +59,7 @@ import (
_ "github.com/go-gost/gost/pkg/listener/http2"
_ "github.com/go-gost/gost/pkg/listener/http2/h2"
_ "github.com/go-gost/gost/pkg/listener/http3"
_ "github.com/go-gost/gost/pkg/listener/icmp"
_ "github.com/go-gost/gost/pkg/listener/kcp"
_ "github.com/go-gost/gost/pkg/listener/obfs/http"
_ "github.com/go-gost/gost/pkg/listener/obfs/tls"
+12 -11
View File
@@ -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
View File
@@ -1,5 +1,5 @@
package main
const (
version = "3.0.0-alpha.1"
version = "3.0.0-alpha.3"
)
+58 -38
View File
@@ -1,73 +1,93 @@
module github.com/go-gost/gost
go 1.17
go 1.18
require (
github.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d
github.com/cheekybits/genny v1.0.0 // indirect
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
github.com/go-gost/tls-dissector v0.0.2-0.20211125135007-2b5d5bd9c07e
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/gobwas/glob v0.2.3
github.com/golang/snappy v0.0.3
github.com/google/gopacket v1.1.19 // indirect
github.com/gorilla/websocket v1.4.2
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/klauspost/cpuid v1.3.1 // indirect
github.com/klauspost/reedsolomon v1.9.9 // indirect
github.com/lucas-clemente/quic-go v0.24.0
github.com/magiconair/properties v1.8.5 // indirect
github.com/marten-seemann/qtls-go1-16 v0.1.4 // indirect
github.com/marten-seemann/qtls-go1-17 v0.1.0 // indirect
github.com/lucas-clemente/quic-go v0.25.0
github.com/miekg/dns v1.1.45
github.com/milosgajdos/tenus v0.0.3
github.com/mitchellh/mapstructure v1.4.2 // indirect
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/onsi/ginkgo v1.16.4 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/prometheus/client_golang v1.12.1
github.com/rs/xid v1.3.0
github.com/shadowsocks/go-shadowsocks2 v0.1.4
github.com/shadowsocks/shadowsocks-go v0.0.0-20200409064450-3e585ff90601
github.com/sirupsen/logrus v1.8.1
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8
github.com/spf13/afero v1.6.0 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.9.0
github.com/subosito/gotenv v1.2.0 // indirect
github.com/templexxx/cpu v0.0.7 // indirect
github.com/templexxx/xorsimd v0.4.1 // indirect
github.com/tjfoc/gmsm v1.3.2 // indirect
github.com/xtaci/kcp-go/v5 v5.6.1
github.com/xtaci/smux v1.5.16
github.com/xtaci/tcpraw v1.2.25
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3
golang.org/x/mod v0.4.2 // indirect
golang.org/x/crypto v0.0.0-20220214200702-86341886e292
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7
google.golang.org/grpc v1.44.0
gopkg.in/ini.v1 v1.63.2 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
google.golang.org/protobuf v1.27.1
gopkg.in/yaml.v2 v2.4.0
)
require (
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cheekybits/genny v1.0.0 // indirect
github.com/coreos/go-iptables v0.5.0 // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.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/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid v1.3.1 // indirect
github.com/klauspost/reedsolomon v1.9.9 // indirect
github.com/leodido/go-urn v1.2.0 // indirect
github.com/magiconair/properties v1.8.5 // indirect
github.com/marten-seemann/qpack v0.2.1 // indirect
github.com/marten-seemann/qtls-go1-16 v0.1.4 // indirect
github.com/marten-seemann/qtls-go1-17 v0.1.0 // indirect
github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.4.2 // indirect
github.com/mmcloughlin/avo v0.0.0-20200803215136-443f81d77104 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/spf13/afero v1.6.0 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/templexxx/cpu v0.0.7 // indirect
github.com/templexxx/xorsimd v0.4.1 // indirect
github.com/tjfoc/gmsm v1.3.2 // indirect
github.com/ugorji/go/codec v1.1.7 // indirect
golang.org/x/mod v0.5.1 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/tools v0.1.9 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/ini.v1 v1.63.2 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
)
+115 -16
View File
@@ -56,6 +56,11 @@ github.com/LiamHaworth/go-tproxy v0.0.0-20190726054950-ef7efd7f24ed/go.mod h1:rA
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
@@ -65,12 +70,17 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ=
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cheekybits/genny v1.0.0 h1:uGGa4nei+j20rOSeDeP5Of12XVm7TGUd4dJA9RDitfE=
github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
@@ -114,6 +124,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 +144,23 @@ 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-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
github.com/go-playground/locales v0.13.0 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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
@@ -246,10 +280,18 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
@@ -257,7 +299,10 @@ github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
github.com/klauspost/reedsolomon v1.9.9 h1:qCL7LZlv17xMixl55nq2/Oa1Y86nfO8EqDfv2GHND54=
github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
@@ -265,8 +310,11 @@ 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/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/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.25.0 h1:K+X9Gvd7JXsOHtU0N2icZ2Nw3rx82uBej3mP4CLgibc=
github.com/lucas-clemente/quic-go v0.25.0/go.mod h1:YtzP8bxRVCBlO77yRanE264+fY/T2U9ZlW1AaHOsMOg=
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
@@ -278,14 +326,19 @@ github.com/marten-seemann/qtls-go1-16 v0.1.4 h1:xbHbOGGhrenVtII6Co8akhLEdrawwB2i
github.com/marten-seemann/qtls-go1-16 v0.1.4/go.mod h1:gNpI2Ol+lRS3WwSOtIUUtRwZEQMXjYK+dQSBFbethAk=
github.com/marten-seemann/qtls-go1-17 v0.1.0 h1:P9ggrs5xtwiqXv/FHNwntmuLMNq3KaSIG93AtAZ48xk=
github.com/marten-seemann/qtls-go1-17 v0.1.0/go.mod h1:fz4HIxByo+LlWcreM4CZOYNuz3taBQ8rN2X6FqvaWo8=
github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1 h1:EnzzN9fPUkUck/1CuY1FlzBaIYMoiBsdwTNmNGkwUUM=
github.com/marten-seemann/qtls-go1-18 v0.1.0-beta.1/go.mod h1:PUhIQk19LoFt2174H4+an8TYvWOGjb/hHwphBeaDHwI=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
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 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
@@ -304,9 +357,14 @@ 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/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
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=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
@@ -316,8 +374,9 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
@@ -326,6 +385,7 @@ github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTm
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -335,10 +395,30 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3/go.mod h1:HgjTstvQsPGkxUsCd2KWxErBblirPizecHcpD3ffK+s=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
@@ -376,6 +456,9 @@ github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1l
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/songgao/water v0.0.0-20200317203138-2b4b6d7c09d8 h1:TG/diQgUe0pntT/2D9tmUCz4VNwm9MfrtPr0SU2qSX8=
@@ -394,6 +477,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/spf13/viper v1.9.0 h1:yR6EXjTp0y0cLN8OZg1CRZmOBdI88UcGkhgyJhu6nZk=
github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -411,6 +495,9 @@ github.com/templexxx/xorsimd v0.4.1 h1:iUZcywbOYDRAZUasAs2eSCUW8eobuZDy0I9FJiORk
github.com/templexxx/xorsimd v0.4.1/go.mod h1:W+ffZz8jJMH2SXwuKu9WhygqBMbFnp14G2fqEr8qaNo=
github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 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=
@@ -444,6 +531,7 @@ go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
golang.org/x/arch v0.0.0-20190909030613-46d78d1859ac/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -459,8 +547,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -496,14 +584,16 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -512,6 +602,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -541,10 +632,8 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-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=
@@ -579,19 +668,23 @@ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cO
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-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=
@@ -602,6 +695,7 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -617,6 +711,8 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200808120158-1030fc2bf1d9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -626,6 +722,7 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -635,17 +732,17 @@ golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7 h1:BXxu8t6QN0G1uff4bzZzSkpsax8+ALqTGUtz08QrV00=
golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7/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/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -655,7 +752,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=
@@ -722,8 +818,9 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 h1:BonxutuHCTL0rBDnZlKjpGIQFTjyUVTexFOdWkB6Fg0=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.9 h1:j9KsMiaP1c3B0OTQGth0/k+miLGTgLsAFUCrF2vLcF8=
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -823,7 +920,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 +950,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=
@@ -873,12 +968,15 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
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=
@@ -888,6 +986,7 @@ gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+85
View File
@@ -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
}
+15
View File
@@ -0,0 +1,15 @@
package api
import (
"embed"
)
var (
//go:embed swagger.yaml
swaggerDoc embed.FS
)
type Response struct {
Code int `json:"code,omitempty"`
Msg string `json:"msg,omitempty"`
}
+118
View File
@@ -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",
})
}
+166
View File
@@ -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.AdmissionRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
cfg.Admissions = append(cfg.Admissions, &req.Data)
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters updateAdmissionRequest
type updateAdmissionRequest struct {
// in: path
// required: true
Admission string `uri:"admission" json:"admission"`
// in: body
Data config.AdmissionConfig `json:"data"`
}
// successful operation.
// swagger:response updateAdmissionResponse
type updateAdmissionResponse struct {
Data Response
}
func updateAdmission(ctx *gin.Context) {
// swagger:route PUT /config/admissions/{admission} ConfigManagement updateAdmissionRequest
//
// Update admission by name, the admission must already exist.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: updateAdmissionResponse
var req updateAdmissionRequest
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.AdmissionRegistry().IsRegistered(req.Admission) {
writeError(ctx, ErrNotFound)
return
}
req.Data.Name = req.Admission
v := parsing.ParseAdmission(&req.Data)
registry.AdmissionRegistry().Unregister(req.Admission)
if err := registry.AdmissionRegistry().Register(req.Admission, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
for i := range cfg.Admissions {
if cfg.Admissions[i].Name == req.Admission {
cfg.Admissions[i] = &req.Data
break
}
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters deleteAdmissionRequest
type deleteAdmissionRequest struct {
// in: path
// required: true
Admission string `uri:"admission" json:"admission"`
}
// successful operation.
// swagger:response deleteAdmissionResponse
type deleteAdmissionResponse struct {
Data Response
}
func deleteAdmission(ctx *gin.Context) {
// swagger:route DELETE /config/admissions/{admission} ConfigManagement deleteAdmissionRequest
//
// Delete admission by name.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: deleteAdmissionResponse
var req deleteAdmissionRequest
ctx.ShouldBindUri(&req)
if !registry.AdmissionRegistry().IsRegistered(req.Admission) {
writeError(ctx, ErrNotFound)
return
}
registry.AdmissionRegistry().Unregister(req.Admission)
cfg := config.Global()
admissiones := cfg.Admissions
cfg.Admissions = nil
for _, s := range admissiones {
if s.Name == req.Admission {
continue
}
cfg.Admissions = append(cfg.Admissions, s)
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
+164
View File
@@ -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.AutherRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
cfg.Authers = append(cfg.Authers, &req.Data)
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters updateAutherRequest
type updateAutherRequest struct {
// in: path
// required: true
Auther string `uri:"auther" json:"auther"`
// in: body
Data config.AutherConfig `json:"data"`
}
// successful operation.
// swagger:response updateAutherResponse
type updateAutherResponse struct {
Data Response
}
func updateAuther(ctx *gin.Context) {
// swagger:route PUT /config/authers/{auther} ConfigManagement updateAutherRequest
//
// Update auther by name, the auther must already exist.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: updateAutherResponse
var req updateAutherRequest
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.AutherRegistry().IsRegistered(req.Auther) {
writeError(ctx, ErrNotFound)
return
}
req.Data.Name = req.Auther
v := parsing.ParseAuther(&req.Data)
registry.AutherRegistry().Unregister(req.Auther)
if err := registry.AutherRegistry().Register(req.Auther, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
for i := range cfg.Authers {
if cfg.Authers[i].Name == req.Auther {
cfg.Authers[i] = &req.Data
break
}
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters deleteAutherRequest
type deleteAutherRequest struct {
// in: path
// required: true
Auther string `uri:"auther" json:"auther"`
}
// successful operation.
// swagger:response deleteAutherResponse
type deleteAutherResponse struct {
Data Response
}
func deleteAuther(ctx *gin.Context) {
// swagger:route DELETE /config/authers/{auther} ConfigManagement deleteAutherRequest
//
// Delete auther by name.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: deleteAutherResponse
var req deleteAutherRequest
ctx.ShouldBindUri(&req)
if !registry.AutherRegistry().IsRegistered(req.Auther) {
writeError(ctx, ErrNotFound)
return
}
registry.AutherRegistry().Unregister(req.Auther)
cfg := config.Global()
authers := cfg.Authers
cfg.Authers = nil
for _, s := range authers {
if s.Name == req.Auther {
continue
}
cfg.Authers = append(cfg.Authers, s)
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
+166
View File
@@ -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.BypassRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
cfg.Bypasses = append(cfg.Bypasses, &req.Data)
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters updateBypassRequest
type updateBypassRequest struct {
// in: path
// required: true
Bypass string `uri:"bypass" json:"bypass"`
// in: body
Data config.BypassConfig `json:"data"`
}
// successful operation.
// swagger:response updateBypassResponse
type updateBypassResponse struct {
Data Response
}
func updateBypass(ctx *gin.Context) {
// swagger:route PUT /config/bypasses/{bypass} ConfigManagement updateBypassRequest
//
// Update bypass by name, the bypass must already exist.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: updateBypassResponse
var req updateBypassRequest
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.BypassRegistry().IsRegistered(req.Bypass) {
writeError(ctx, ErrNotFound)
return
}
req.Data.Name = req.Bypass
v := parsing.ParseBypass(&req.Data)
registry.BypassRegistry().Unregister(req.Bypass)
if err := registry.BypassRegistry().Register(req.Bypass, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
for i := range cfg.Bypasses {
if cfg.Bypasses[i].Name == req.Bypass {
cfg.Bypasses[i] = &req.Data
break
}
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters deleteBypassRequest
type deleteBypassRequest struct {
// in: path
// required: true
Bypass string `uri:"bypass" json:"bypass"`
}
// successful operation.
// swagger:response deleteBypassResponse
type deleteBypassResponse struct {
Data Response
}
func deleteBypass(ctx *gin.Context) {
// swagger:route DELETE /config/bypasses/{bypass} ConfigManagement deleteBypassRequest
//
// Delete bypass by name.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: deleteBypassResponse
var req deleteBypassRequest
ctx.ShouldBindUri(&req)
if !registry.BypassRegistry().IsRegistered(req.Bypass) {
writeError(ctx, ErrNotFound)
return
}
registry.BypassRegistry().Unregister(req.Bypass)
cfg := config.Global()
bypasses := cfg.Bypasses
cfg.Bypasses = nil
for _, s := range bypasses {
if s.Name == req.Bypass {
continue
}
cfg.Bypasses = append(cfg.Bypasses, s)
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
+175
View File
@@ -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.ChainRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
cfg.Chains = append(cfg.Chains, &req.Data)
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters updateChainRequest
type updateChainRequest struct {
// in: path
// required: true
// chain name
Chain string `uri:"chain" json:"chain"`
// in: body
Data config.ChainConfig `json:"data"`
}
// successful operation.
// swagger:response updateChainResponse
type updateChainResponse struct {
Data Response
}
func updateChain(ctx *gin.Context) {
// swagger:route PUT /config/chains/{chain} ConfigManagement updateChainRequest
//
// Update chain by name, the chain must already exist.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: updateChainResponse
var req updateChainRequest
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.ChainRegistry().IsRegistered(req.Chain) {
writeError(ctx, ErrNotFound)
return
}
req.Data.Name = req.Chain
v, err := parsing.ParseChain(&req.Data)
if err != nil {
writeError(ctx, ErrCreate)
return
}
registry.ChainRegistry().Unregister(req.Chain)
if err := registry.ChainRegistry().Register(req.Chain, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
for i := range cfg.Chains {
if cfg.Chains[i].Name == req.Chain {
cfg.Chains[i] = &req.Data
break
}
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters deleteChainRequest
type deleteChainRequest struct {
// in: path
// required: true
Chain string `uri:"chain" json:"chain"`
}
// successful operation.
// swagger:response deleteChainResponse
type deleteChainResponse struct {
Data Response
}
func deleteChain(ctx *gin.Context) {
// swagger:route DELETE /config/chains/{chain} ConfigManagement deleteChainRequest
//
// Delete chain by name.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: deleteChainResponse
var req deleteChainRequest
ctx.ShouldBindUri(&req)
if !registry.ChainRegistry().IsRegistered(req.Chain) {
writeError(ctx, ErrNotFound)
return
}
registry.ChainRegistry().Unregister(req.Chain)
cfg := config.Global()
chains := cfg.Chains
cfg.Chains = nil
for _, s := range chains {
if s.Name == req.Chain {
continue
}
cfg.Chains = append(cfg.Chains, s)
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
+166
View File
@@ -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.HostsRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
cfg.Hosts = append(cfg.Hosts, &req.Data)
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters updateHostsRequest
type updateHostsRequest struct {
// in: path
// required: true
Hosts string `uri:"hosts" json:"hosts"`
// in: body
Data config.HostsConfig `json:"data"`
}
// successful operation.
// swagger:response updateHostsResponse
type updateHostsResponse struct {
Data Response
}
func updateHosts(ctx *gin.Context) {
// swagger:route PUT /config/hosts/{hosts} ConfigManagement updateHostsRequest
//
// Update hosts by name, the hosts must already exist.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: updateHostsResponse
var req updateHostsRequest
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.HostsRegistry().IsRegistered(req.Hosts) {
writeError(ctx, ErrNotFound)
return
}
req.Data.Name = req.Hosts
v := parsing.ParseHosts(&req.Data)
registry.HostsRegistry().Unregister(req.Hosts)
if err := registry.HostsRegistry().Register(req.Hosts, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
for i := range cfg.Hosts {
if cfg.Hosts[i].Name == req.Hosts {
cfg.Hosts[i] = &req.Data
break
}
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters deleteHostsRequest
type deleteHostsRequest struct {
// in: path
// required: true
Hosts string `uri:"hosts" json:"hosts"`
}
// successful operation.
// swagger:response deleteHostsResponse
type deleteHostsResponse struct {
Data Response
}
func deleteHosts(ctx *gin.Context) {
// swagger:route DELETE /config/hosts/{hosts} ConfigManagement deleteHostsRequest
//
// Delete hosts by name.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: deleteHostsResponse
var req deleteHostsRequest
ctx.ShouldBindUri(&req)
if !registry.HostsRegistry().IsRegistered(req.Hosts) {
writeError(ctx, ErrNotFound)
return
}
registry.HostsRegistry().Unregister(req.Hosts)
cfg := config.Global()
hosts := cfg.Hosts
cfg.Hosts = nil
for _, s := range hosts {
if s.Name == req.Hosts {
continue
}
cfg.Hosts = append(cfg.Hosts, s)
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
+174
View File
@@ -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.ResolverRegistry().Register(req.Data.Name, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
cfg.Resolvers = append(cfg.Resolvers, &req.Data)
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters updateResolverRequest
type updateResolverRequest struct {
// in: path
// required: true
Resolver string `uri:"resolver" json:"resolver"`
// in: body
Data config.ResolverConfig `json:"data"`
}
// successful operation.
// swagger:response updateResolverResponse
type updateResolverResponse struct {
Data Response
}
func updateResolver(ctx *gin.Context) {
// swagger:route PUT /config/resolvers/{resolver} ConfigManagement updateResolverRequest
//
// Update resolver by name, the resolver must already exist.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: updateResolverResponse
var req updateResolverRequest
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
if !registry.ResolverRegistry().IsRegistered(req.Resolver) {
writeError(ctx, ErrNotFound)
return
}
req.Data.Name = req.Resolver
v, err := parsing.ParseResolver(&req.Data)
if err != nil {
writeError(ctx, ErrCreate)
return
}
registry.ResolverRegistry().Unregister(req.Resolver)
if err := registry.ResolverRegistry().Register(req.Resolver, v); err != nil {
writeError(ctx, ErrDup)
return
}
cfg := config.Global()
for i := range cfg.Resolvers {
if cfg.Resolvers[i].Name == req.Resolver {
cfg.Resolvers[i] = &req.Data
break
}
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters deleteResolverRequest
type deleteResolverRequest struct {
// in: path
// required: true
Resolver string `uri:"resolver" json:"resolver"`
}
// successful operation.
// swagger:response deleteResolverResponse
type deleteResolverResponse struct {
Data Response
}
func deleteResolver(ctx *gin.Context) {
// swagger:route DELETE /config/resolvers/{resolver} ConfigManagement deleteResolverRequest
//
// Delete resolver by name.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: deleteResolverResponse
var req deleteResolverRequest
ctx.ShouldBindUri(&req)
if !registry.ResolverRegistry().IsRegistered(req.Resolver) {
writeError(ctx, ErrNotFound)
return
}
registry.ResolverRegistry().Unregister(req.Resolver)
cfg := config.Global()
resolvers := cfg.Resolvers
cfg.Resolvers = nil
for _, s := range resolvers {
if s.Name == req.Resolver {
continue
}
cfg.Resolvers = append(cfg.Resolvers, s)
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
+190
View File
@@ -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.ServiceRegistry().IsRegistered(req.Data.Name) {
writeError(ctx, ErrDup)
return
}
svc, err := parsing.ParseService(&req.Data)
if err != nil {
writeError(ctx, ErrCreate)
return
}
if err := registry.ServiceRegistry().Register(req.Data.Name, svc); err != nil {
svc.Close()
writeError(ctx, ErrDup)
return
}
go svc.Serve()
cfg := config.Global()
cfg.Services = append(cfg.Services, &req.Data)
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters updateServiceRequest
type updateServiceRequest struct {
// in: path
// required: true
Service string `uri:"service" json:"service"`
// in: body
Data config.ServiceConfig `json:"data"`
}
// successful operation.
// swagger:response updateServiceResponse
type updateServiceResponse struct {
Data Response
}
func updateService(ctx *gin.Context) {
// swagger:route PUT /config/services/{service} ConfigManagement updateServiceRequest
//
// Update service by name, the service must already exist.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: updateServiceResponse
var req updateServiceRequest
ctx.ShouldBindUri(&req)
ctx.ShouldBindJSON(&req.Data)
old := registry.ServiceRegistry().Get(req.Service)
if old == nil {
writeError(ctx, ErrNotFound)
return
}
old.Close()
req.Data.Name = req.Service
svc, err := parsing.ParseService(&req.Data)
if err != nil {
writeError(ctx, ErrCreate)
return
}
registry.ServiceRegistry().Unregister(req.Service)
if err := registry.ServiceRegistry().Register(req.Service, svc); err != nil {
svc.Close()
writeError(ctx, ErrDup)
return
}
go svc.Serve()
cfg := config.Global()
for i := range cfg.Services {
if cfg.Services[i].Name == req.Service {
cfg.Services[i] = &req.Data
break
}
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
// swagger:parameters deleteServiceRequest
type deleteServiceRequest struct {
// in: path
// required: true
Service string `uri:"service" json:"service"`
}
// successful operation.
// swagger:response deleteServiceResponse
type deleteServiceResponse struct {
Data Response
}
func deleteService(ctx *gin.Context) {
// swagger:route DELETE /config/services/{service} ConfigManagement deleteServiceRequest
//
// Delete service by name.
//
// Security:
// basicAuth: []
//
// Responses:
// 200: deleteServiceResponse
var req deleteServiceRequest
ctx.ShouldBindUri(&req)
svc := registry.ServiceRegistry().Get(req.Service)
if svc == nil {
writeError(ctx, ErrNotFound)
return
}
registry.ServiceRegistry().Unregister(req.Service)
svc.Close()
cfg := config.Global()
services := cfg.Services
cfg.Services = nil
for _, s := range services {
if s.Name == req.Service {
continue
}
cfg.Services = append(cfg.Services, s)
}
config.SetGlobal(cfg)
ctx.JSON(http.StatusOK, Response{
Msg: "OK",
})
}
+18
View File
@@ -0,0 +1,18 @@
// Documentation of Web API.
//
// Schemes: https, http
// BasePath: /
// Version: 1.0.0
//
// Consumes:
// - application/json
//
// Produces:
// - application/json
//
// SecurityDefinitions:
// basicAuth:
// type: basic
//
// swagger:meta
package api
+45
View File
@@ -0,0 +1,45 @@
package api
import (
"encoding/json"
"net/http"
"github.com/gin-gonic/gin"
)
var (
ErrInvalid = &Error{statusCode: http.StatusBadRequest, Code: 40001, Msg: "object invalid"}
ErrDup = &Error{statusCode: http.StatusBadRequest, Code: 40002, Msg: "object duplicated"}
ErrCreate = &Error{statusCode: http.StatusConflict, Code: 40003, Msg: "object creation failed"}
ErrNotFound = &Error{statusCode: http.StatusBadRequest, Code: 40004, Msg: "object not found"}
ErrSave = &Error{statusCode: http.StatusInternalServerError, Code: 40005, Msg: "save config failed"}
)
// Error is an api error.
type Error struct {
statusCode int
Code int `json:"code"`
Msg string `json:"msg"`
}
func (e *Error) Error() string {
b, _ := json.Marshal(e)
return string(b)
}
func writeError(c *gin.Context, err error) {
// c.Set(HTTPResponseTag, err)
c.JSON(getStatusCode(err), err)
}
func getStatusCode(err error) int {
if err == nil {
return http.StatusOK
}
if e, ok := err.(*Error); ok {
if e.statusCode >= http.StatusOK && e.statusCode < 600 {
return e.statusCode
}
}
return http.StatusInternalServerError
}
+42
View File
@@ -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]any{
"kind": "api",
"method": ctx.Request.Method,
"uri": ctx.Request.RequestURI,
"code": ctx.Writer.Status(),
"client": ctx.ClientIP(),
"duration": duration,
}).Infof("| %3d | %13v | %15s | %-7s %s",
ctx.Writer.Status(), duration, ctx.ClientIP(), ctx.Request.Method, ctx.Request.RequestURI)
}
}
func mwBasicAuth(auther auth.Authenticator) gin.HandlerFunc {
return func(c *gin.Context) {
if auther == nil {
return
}
u, p, _ := c.Request.BasicAuth()
if !auther.Authenticate(u, p) {
c.AbortWithStatus(http.StatusUnauthorized)
}
}
}
+132
View File
@@ -0,0 +1,132 @@
package api
import (
"net"
"net/http"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
"github.com/go-gost/gost/pkg/auth"
"github.com/go-gost/gost/pkg/service"
)
type options struct {
accessLog bool
pathPrefix string
auther auth.Authenticator
}
type Option func(*options)
func PathPrefixOption(pathPrefix string) Option {
return func(o *options) {
o.pathPrefix = pathPrefix
}
}
func AccessLogOption(enable bool) Option {
return func(o *options) {
o.accessLog = enable
}
}
func AutherOption(auther auth.Authenticator) Option {
return func(o *options) {
o.auther = auther
}
}
type server struct {
s *http.Server
ln net.Listener
}
func NewService(addr string, opts ...Option) (service.Service, error) {
ln, err := net.Listen("tcp", addr)
if err != nil {
return nil, err
}
var options options
for _, opt := range opts {
opt(&options)
}
gin.SetMode(gin.ReleaseMode)
r := gin.New()
r.Use(
cors.New((cors.Config{
AllowAllOrigins: true,
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"*"},
})),
gin.Recovery(),
)
if options.accessLog {
r.Use(mwLogger())
}
router := r.Group("")
if options.pathPrefix != "" {
router = router.Group(options.pathPrefix)
}
router.StaticFS("/docs", http.FS(swaggerDoc))
config := router.Group("/config")
config.Use(mwBasicAuth(options.auther))
registerConfig(config)
return &server{
s: &http.Server{
Handler: r,
},
ln: ln,
}, nil
}
func (s *server) Serve() error {
return s.s.Serve(s.ln)
}
func (s *server) Addr() net.Addr {
return s.ln.Addr()
}
func (s *server) Close() error {
return s.s.Close()
}
func registerConfig(config *gin.RouterGroup) {
config.GET("", getConfig)
config.POST("", saveConfig)
config.POST("/services", createService)
config.PUT("/services/:service", updateService)
config.DELETE("/services/:service", deleteService)
config.POST("/chains", createChain)
config.PUT("/chains/:chain", updateChain)
config.DELETE("/chains/:chain", deleteChain)
config.POST("/authers", createAuther)
config.PUT("/authers/:auther", updateAuther)
config.DELETE("/authers/:auther", deleteAuther)
config.POST("/admissions", createAdmission)
config.PUT("/admissions/:admission", updateAdmission)
config.DELETE("/admissions/:admission", deleteAdmission)
config.POST("/bypasses", createBypass)
config.PUT("/bypasses/:bypass", updateBypass)
config.DELETE("/bypasses/:bypass", deleteBypass)
config.POST("/resolvers", createResolver)
config.PUT("/resolvers/:resolver", updateResolver)
config.DELETE("/resolvers/:resolver", deleteResolver)
config.POST("/hosts", createHosts)
config.PUT("/hosts/:hosts", updateHosts)
config.DELETE("/hosts/:hosts", deleteHosts)
}
+1068
View File
File diff suppressed because it is too large Load Diff
+12 -104
View File
@@ -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
View File
@@ -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
}
+7 -4
View File
@@ -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 {
+36 -36
View File
@@ -5,7 +5,9 @@ import (
"errors"
"fmt"
"net"
"time"
"github.com/go-gost/gost/pkg/common/net/dialer"
"github.com/go-gost/gost/pkg/common/util/udp"
"github.com/go-gost/gost/pkg/connector"
"github.com/go-gost/gost/pkg/logger"
@@ -15,18 +17,25 @@ var (
ErrEmptyRoute = errors.New("empty route")
)
type route struct {
nodes []*Node
logger logger.Logger
type Route struct {
nodes []*Node
ifceName string
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() {
return r.dialDirect(ctx, network, address)
func (r *Route) Dial(ctx context.Context, network, address string) (net.Conn, error) {
if r.Len() == 0 {
netd := dialer.NetDialer{
Timeout: 30 * time.Second,
}
if r != nil {
netd.Interface = r.ifceName
}
return netd.Dial(ctx, network, address)
}
conn, err := r.connect(ctx)
@@ -34,7 +43,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,21 +51,8 @@ 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) {
switch network {
case "udp", "udp4", "udp6":
if address == "" {
return net.ListenUDP(network, nil)
}
default:
}
d := net.Dialer{}
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 +61,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 +70,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 +99,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 +127,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 +155,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)
@@ -177,7 +177,7 @@ func (r *route) bindLocal(ctx context.Context, network, address string, opts ...
if err != nil {
return nil, err
}
logger := logger.Default().WithFields(map[string]interface{}{
logger := logger.Default().WithFields(map[string]any{
"network": network,
"address": address,
})
+74 -21
View File
@@ -5,6 +5,7 @@ import (
"context"
"fmt"
"net"
"time"
"github.com/go-gost/gost/pkg/connector"
"github.com/go-gost/gost/pkg/hosts"
@@ -13,11 +14,55 @@ import (
)
type Router struct {
Retries int
Chain *Chain
Hosts hosts.HostMapper
Resolver resolver.Resolver
Logger logger.Logger
timeout time.Duration
retries int
ifceName string
chain Chainer
resolver resolver.Resolver
hosts hosts.HostMapper
logger logger.Logger
}
func (r *Router) WithTimeout(timeout time.Duration) *Router {
r.timeout = timeout
return r
}
func (r *Router) WithRetries(retries int) *Router {
r.retries = retries
return r
}
func (r *Router) WithInterface(ifceName string) *Router {
r.ifceName = ifceName
return r
}
func (r *Router) WithChain(chain Chainer) *Router {
r.chain = chain
return r
}
func (r *Router) WithResolver(resolver resolver.Resolver) *Router {
r.resolver = resolver
return r
}
func (r *Router) WithHosts(hosts hosts.HostMapper) *Router {
r.hosts = hosts
return r
}
func (r *Router) Hosts() hosts.HostMapper {
if r != nil {
return r.hosts
}
return nil
}
func (r *Router) WithLogger(logger logger.Logger) *Router {
r.logger = logger
return r
}
func (r *Router) Dial(ctx context.Context, network, address string) (conn net.Conn, err error) {
@@ -34,68 +79,76 @@ func (r *Router) Dial(ctx context.Context, network, address string) (conn net.Co
}
func (r *Router) dial(ctx context.Context, network, address string) (conn net.Conn, err error) {
count := r.Retries + 1
count := r.retries + 1
if count <= 0 {
count = 1
}
r.Logger.Debugf("dial %s/%s", address, network)
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) {
if r.logger.IsLevelEnabled(logger.DebugLevel) {
buf := bytes.Buffer{}
for _, node := range route.Path() {
fmt.Fprintf(&buf, "%s@%s > ", node.Name, node.Addr)
}
fmt.Fprintf(&buf, "%s", address)
r.Logger.Debugf("route(retry=%d) %s", i, buf.String())
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)
r.logger.Error(err)
break
}
if route != nil {
route.logger = r.Logger
if route == nil {
route = &Route{}
}
route.ifceName = r.ifceName
route.logger = r.logger
conn, err = route.Dial(ctx, network, address)
if err == nil {
break
}
r.Logger.Errorf("route(retry=%d) %s", i, err)
r.logger.Errorf("route(retry=%d) %s", i, err)
}
return
}
func (r *Router) Bind(ctx context.Context, network, address string, opts ...connector.BindOption) (ln net.Listener, err error) {
count := r.Retries + 1
count := r.retries + 1
if count <= 0 {
count = 1
}
r.Logger.Debugf("bind on %s/%s", address, network)
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) {
if r.logger.IsLevelEnabled(logger.DebugLevel) {
buf := bytes.Buffer{}
for _, node := range route.Path() {
fmt.Fprintf(&buf, "%s@%s > ", node.Name, node.Addr)
}
fmt.Fprintf(&buf, "%s", address)
r.Logger.Debugf("route(retry=%d) %s", i, buf.String())
r.logger.Debugf("route(retry=%d) %s", i, buf.String())
}
ln, err = route.Bind(ctx, network, address, opts...)
if err == nil {
break
}
r.Logger.Errorf("route(retry=%d) %s", i, err)
r.logger.Errorf("route(retry=%d) %s", i, err)
}
return
+21 -16
View File
@@ -3,14 +3,17 @@ package chain
import (
"context"
"net"
"time"
net_dialer "github.com/go-gost/gost/pkg/common/net/dialer"
"github.com/go-gost/gost/pkg/connector"
"github.com/go-gost/gost/pkg/dialer"
)
type Transport struct {
addr string
route *route
ifceName string
route *Route
dialer dialer.Dialer
connector connector.Connector
}
@@ -21,6 +24,11 @@ func (tr *Transport) Copy() *Transport {
return tr
}
func (tr *Transport) WithInterface(ifceName string) *Transport {
tr.ifceName = ifceName
return tr
}
func (tr *Transport) WithDialer(dialer dialer.Dialer) *Transport {
tr.dialer = dialer
return tr
@@ -32,23 +40,20 @@ func (tr *Transport) WithConnector(connector connector.Connector) *Transport {
}
func (tr *Transport) Dial(ctx context.Context, addr string) (net.Conn, error) {
return tr.dialer.Dial(ctx, addr, tr.dialOptions()...)
}
func (tr *Transport) dialOptions() []dialer.DialOption {
netd := &net_dialer.NetDialer{
Interface: tr.ifceName,
Timeout: 30 * time.Second,
}
if tr.route.Len() > 0 {
netd.DialFunc = func(ctx context.Context, network, addr string) (net.Conn, error) {
return tr.route.Dial(ctx, network, addr)
}
}
opts := []dialer.DialOption{
dialer.HostDialOption(tr.addr),
dialer.NetDialerDialOption(netd),
}
if !tr.route.IsEmpty() {
opts = append(opts,
dialer.DialFuncDialOption(
func(ctx context.Context, addr string) (net.Conn, error) {
return tr.route.Dial(ctx, "tcp", addr)
},
),
)
}
return opts
return tr.dialer.Dial(ctx, addr, opts...)
}
func (tr *Transport) Handshake(ctx context.Context, conn net.Conn) (net.Conn, error) {
@@ -84,7 +89,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
}
+9 -9
View File
@@ -10,7 +10,7 @@ var (
{
size: 128,
pool: sync.Pool{
New: func() interface{} {
New: func() any {
b := make([]byte, 128)
return &b
},
@@ -19,7 +19,7 @@ var (
{
size: 512,
pool: sync.Pool{
New: func() interface{} {
New: func() any {
b := make([]byte, 512)
return &b
},
@@ -28,7 +28,7 @@ var (
{
size: 1024,
pool: sync.Pool{
New: func() interface{} {
New: func() any {
b := make([]byte, 1024)
return &b
},
@@ -37,7 +37,7 @@ var (
{
size: 4096,
pool: sync.Pool{
New: func() interface{} {
New: func() any {
b := make([]byte, 4096)
return &b
},
@@ -46,7 +46,7 @@ var (
{
size: 8192,
pool: sync.Pool{
New: func() interface{} {
New: func() any {
b := make([]byte, 8192)
return &b
},
@@ -55,7 +55,7 @@ var (
{
size: 16 * 1024,
pool: sync.Pool{
New: func() interface{} {
New: func() any {
b := make([]byte, 16*1024)
return &b
},
@@ -64,7 +64,7 @@ var (
{
size: 32 * 1024,
pool: sync.Pool{
New: func() interface{} {
New: func() any {
b := make([]byte, 32*1024)
return &b
},
@@ -73,7 +73,7 @@ var (
{
size: 64 * 1024,
pool: sync.Pool{
New: func() interface{} {
New: func() any {
b := make([]byte, 64*1024)
return &b
},
@@ -82,7 +82,7 @@ var (
{
size: 65 * 1024,
pool: sync.Pool{
New: func() interface{} {
New: func() any {
b := make([]byte, 65*1024)
return &b
},
+99
View File
@@ -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)
}
+222
View File
@@ -0,0 +1,222 @@
package metrics
import (
"errors"
"io"
"net"
"syscall"
"github.com/go-gost/gost/pkg/metrics"
)
var (
errUnsupport = errors.New("unsupported operation")
)
// ServerConn is a server side Conn with metrics supported.
type serverConn struct {
net.Conn
service string
}
func WrapConn(service string, c net.Conn) net.Conn {
return &serverConn{
service: service,
Conn: c,
}
}
func (c *serverConn) Read(b []byte) (n int, err error) {
n, err = c.Conn.Read(b)
metrics.InputBytes(c.service).Add(float64(n))
return
}
func (c *serverConn) Write(b []byte) (n int, err error) {
n, err = c.Conn.Write(b)
metrics.OutputBytes(c.service).Add(float64(n))
return
}
type packetConn struct {
net.PacketConn
service string
}
func WrapPacketConn(service string, pc net.PacketConn) net.PacketConn {
return &packetConn{
PacketConn: pc,
service: service,
}
}
func (c *packetConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
n, addr, err = c.PacketConn.ReadFrom(p)
metrics.InputBytes(c.service).Add(float64(n))
return
}
func (c *packetConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
n, err = c.PacketConn.WriteTo(p, addr)
metrics.OutputBytes(c.service).Add(float64(n))
return
}
type udpConn struct {
net.PacketConn
service string
}
func WrapUDPConn(service string, pc net.PacketConn) UDPConn {
return &udpConn{
PacketConn: pc,
service: service,
}
}
func (c *udpConn) RemoteAddr() net.Addr {
if nc, ok := c.PacketConn.(remoteAddr); ok {
return nc.RemoteAddr()
}
return nil
}
func (c *udpConn) SetReadBuffer(n int) error {
if nc, ok := c.PacketConn.(setBuffer); ok {
return nc.SetReadBuffer(n)
}
return errUnsupport
}
func (c *udpConn) SetWriteBuffer(n int) error {
if nc, ok := c.PacketConn.(setBuffer); ok {
return nc.SetWriteBuffer(n)
}
return errUnsupport
}
func (c *udpConn) Read(b []byte) (n int, err error) {
if nc, ok := c.PacketConn.(io.Reader); ok {
n, err = nc.Read(b)
metrics.InputBytes(c.service).Add(float64(n))
return
}
err = errUnsupport
return
}
func (c *udpConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
n, addr, err = c.PacketConn.ReadFrom(p)
metrics.InputBytes(c.service).Add(float64(n))
return
}
func (c *udpConn) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) {
if nc, ok := c.PacketConn.(readUDP); ok {
n, addr, err = nc.ReadFromUDP(b)
metrics.InputBytes(c.service).Add(float64(n))
return
}
err = errUnsupport
return
}
func (c *udpConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error) {
if nc, ok := c.PacketConn.(readUDP); ok {
n, oobn, flags, addr, err = nc.ReadMsgUDP(b, oob)
metrics.InputBytes(c.service).Add(float64(n + oobn))
return
}
err = errUnsupport
return
}
func (c *udpConn) Write(b []byte) (n int, err error) {
if nc, ok := c.PacketConn.(io.Writer); ok {
n, err = nc.Write(b)
metrics.OutputBytes(c.service).Add(float64(n))
return
}
err = errUnsupport
return
}
func (c *udpConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
n, err = c.PacketConn.WriteTo(p, addr)
metrics.OutputBytes(c.service).Add(float64(n))
return
}
func (c *udpConn) WriteToUDP(b []byte, addr *net.UDPAddr) (n int, err error) {
if nc, ok := c.PacketConn.(writeUDP); ok {
n, err = nc.WriteToUDP(b, addr)
metrics.OutputBytes(c.service).Add(float64(n))
return
}
err = errUnsupport
return
}
func (c *udpConn) WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error) {
if nc, ok := c.PacketConn.(writeUDP); ok {
n, oobn, err = nc.WriteMsgUDP(b, oob, addr)
metrics.OutputBytes(c.service).Add(float64(n + oobn))
return
}
err = errUnsupport
return
}
func (c *udpConn) SyscallConn() (rc syscall.RawConn, err error) {
if nc, ok := c.PacketConn.(syscallConn); ok {
return nc.SyscallConn()
}
err = errUnsupport
return
}
func (c *udpConn) SetDSCP(n int) error {
if nc, ok := c.PacketConn.(setDSCP); ok {
return nc.SetDSCP(n)
}
return nil
}
type UDPConn interface {
net.PacketConn
io.Reader
io.Writer
readUDP
writeUDP
setBuffer
syscallConn
remoteAddr
}
type setBuffer interface {
SetReadBuffer(bytes int) error
SetWriteBuffer(bytes int) error
}
type readUDP interface {
ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error)
ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error)
}
type writeUDP interface {
WriteToUDP(b []byte, addr *net.UDPAddr) (int, error)
WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error)
}
type syscallConn interface {
SyscallConn() (syscall.RawConn, error)
}
type remoteAddr interface {
RemoteAddr() net.Addr
}
// tcpraw.TCPConn
type setDSCP interface {
SetDSCP(int) error
}
+23
View File
@@ -0,0 +1,23 @@
package metrics
import "net"
type listener struct {
service string
net.Listener
}
func WrapListener(service string, ln net.Listener) net.Listener {
return &listener{
service: service,
Listener: ln,
}
}
func (ln *listener) Accept() (net.Conn, error) {
c, err := ln.Listener.Accept()
if err != nil {
return nil, err
}
return WrapConn(ln.service, c), nil
}
+144
View File
@@ -0,0 +1,144 @@
package dialer
import (
"context"
"fmt"
"net"
"syscall"
"time"
"github.com/go-gost/gost/pkg/logger"
)
var (
DefaultNetDialer = &NetDialer{
Timeout: 30 * time.Second,
}
)
type NetDialer struct {
Interface string
Timeout time.Duration
DialFunc func(ctx context.Context, network, addr string) (net.Conn, error)
Logger logger.Logger
}
func (d *NetDialer) Dial(ctx context.Context, network, addr string) (net.Conn, error) {
if d == nil {
d = DefaultNetDialer
}
log := d.Logger
if log == nil {
log = logger.Default()
}
ifceName, ifAddr, err := parseInterfaceAddr(d.Interface, network)
if err != nil {
return nil, err
}
if d.DialFunc != nil {
return d.DialFunc(ctx, network, addr)
}
logger.Default().Infof("interface: %s %v/%s", ifceName, ifAddr, network)
switch network {
case "udp", "udp4", "udp6":
if addr == "" {
var laddr *net.UDPAddr
if ifAddr != nil {
laddr, _ = ifAddr.(*net.UDPAddr)
}
return net.ListenUDP(network, laddr)
}
case "tcp", "tcp4", "tcp6":
default:
return nil, fmt.Errorf("dial: unsupported network %s", network)
}
netd := net.Dialer{
Timeout: d.Timeout,
LocalAddr: ifAddr,
Control: func(network, address string, c syscall.RawConn) error {
var cerr error
err := c.Control(func(fd uintptr) {
cerr = bindDevice(fd, ifceName)
})
if err != nil {
return err
}
if cerr != nil {
return cerr
}
return nil
},
}
return netd.DialContext(ctx, network, addr)
}
func parseInterfaceAddr(ifceName, network string) (ifce string, addr net.Addr, err error) {
if ifceName == "" {
return
}
ip := net.ParseIP(ifceName)
if ip == nil {
var ife *net.Interface
ife, err = net.InterfaceByName(ifceName)
if err != nil {
return
}
var addrs []net.Addr
addrs, err = ife.Addrs()
if err != nil {
return
}
if len(addrs) == 0 {
err = fmt.Errorf("addr not found for interface %s", ifceName)
return
}
ip = addrs[0].(*net.IPNet).IP
ifce = ifceName
} else {
ifce, err = findInterfaceByIP(ip)
if err != nil {
return
}
}
port := 0
switch network {
case "tcp", "tcp4", "tcp6":
addr = &net.TCPAddr{IP: ip, Port: port}
return
case "udp", "udp4", "udp6":
addr = &net.UDPAddr{IP: ip, Port: port}
return
default:
addr = &net.IPAddr{IP: ip}
return
}
}
func findInterfaceByIP(ip net.IP) (string, error) {
ifces, err := net.Interfaces()
if err != nil {
return "", err
}
for _, ifce := range ifces {
addrs, _ := ifce.Addrs()
if len(addrs) == 0 {
continue
}
for _, addr := range addrs {
ipAddr, _ := addr.(*net.IPNet)
if ipAddr == nil {
continue
}
// logger.Default().Infof("%s-%s", ipAddr, ip)
if ipAddr.IP.Equal(ip) {
return ifce.Name, nil
}
}
}
return "", nil
}
+14
View File
@@ -0,0 +1,14 @@
package dialer
import (
"golang.org/x/sys/unix"
)
func bindDevice(fd uintptr, ifceName string) error {
// unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)
// unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)
if ifceName == "" {
return nil
}
return unix.BindToDevice(int(fd), ifceName)
}
+7
View File
@@ -0,0 +1,7 @@
//go:build !linux
package dialer
func bindDevice(fd uintptr, ifceName string) error {
return nil
}
@@ -1,4 +1,4 @@
package handler
package relay
import (
"net"
@@ -1,4 +1,4 @@
package handler
package net
import (
"bufio"
-24
View File
@@ -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
}
+2 -2
View File
@@ -95,7 +95,7 @@ func file_gost_proto_rawDescGZIP() []byte {
}
var file_gost_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_gost_proto_goTypes = []interface{}{
var file_gost_proto_goTypes = []any{
(*Chunk)(nil), // 0: Chunk
}
var file_gost_proto_depIdxs = []int32{
@@ -114,7 +114,7 @@ func file_gost_proto_init() {
return
}
if !protoimpl.UnsafeEnabled {
file_gost_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
file_gost_proto_msgTypes[0].Exporter = func(v any, i int) any {
switch v := v.(*Chunk); i {
case 0:
return &v.state
+1 -1
View File
@@ -88,7 +88,7 @@ func RegisterGostTunelServer(s grpc.ServiceRegistrar, srv GostTunelServer) {
s.RegisterService(&GostTunel_ServiceDesc, srv)
}
func _GostTunel_Tunnel_Handler(srv interface{}, stream grpc.ServerStream) error {
func _GostTunel_Tunnel_Handler(srv any, stream grpc.ServerStream) error {
return srv.(GostTunelServer).Tunnel(&gostTunelTunnelServer{stream})
}
+6 -6
View File
@@ -38,7 +38,7 @@ func (session *Session) GetConn() (net.Conn, error) {
if err != nil {
return nil, err
}
return &StreamConn{Conn: session.conn, stream: stream}, nil
return &streamConn{Conn: session.conn, stream: stream}, nil
}
func (session *Session) Accept() (net.Conn, error) {
@@ -46,7 +46,7 @@ func (session *Session) Accept() (net.Conn, error) {
if err != nil {
return nil, err
}
return &StreamConn{Conn: session.conn, stream: stream}, nil
return &streamConn{Conn: session.conn, stream: stream}, nil
}
func (session *Session) Close() error {
@@ -67,19 +67,19 @@ func (session *Session) NumStreams() int {
return session.session.NumStreams()
}
type StreamConn struct {
type streamConn struct {
net.Conn
stream *smux.Stream
}
func (c *StreamConn) Read(b []byte) (n int, err error) {
func (c *streamConn) Read(b []byte) (n int, err error) {
return c.stream.Read(b)
}
func (c *StreamConn) Write(b []byte) (n int, err error) {
func (c *streamConn) Write(b []byte) (n int, err error) {
return c.stream.Write(b)
}
func (c *StreamConn) Close() error {
func (c *streamConn) Close() error {
return c.stream.Close()
}
+5 -5
View File
@@ -28,7 +28,7 @@ func (p *ConnPool) WithLogger(logger logger.Logger) *ConnPool {
return p
}
func (p *ConnPool) Get(key interface{}) (c *Conn, ok bool) {
func (p *ConnPool) Get(key any) (c *Conn, ok bool) {
v, ok := p.m.Load(key)
if ok {
c, ok = v.(*Conn)
@@ -36,11 +36,11 @@ func (p *ConnPool) Get(key interface{}) (c *Conn, ok bool) {
return
}
func (p *ConnPool) Set(key interface{}, c *Conn) {
func (p *ConnPool) Set(key any, c *Conn) {
p.m.Store(key, c)
}
func (p *ConnPool) Delete(key interface{}) {
func (p *ConnPool) Delete(key any) {
p.m.Delete(key)
}
@@ -53,7 +53,7 @@ func (p *ConnPool) Close() {
close(p.closed)
p.m.Range(func(k, v interface{}) bool {
p.m.Range(func(k, v any) bool {
if c, ok := v.(*Conn); ok && c != nil {
c.Close()
}
@@ -70,7 +70,7 @@ func (p *ConnPool) idleCheck() {
case <-ticker.C:
size := 0
idles := 0
p.m.Range(func(key, value interface{}) bool {
p.m.Range(func(key, value any) bool {
c, ok := value.(*Conn)
if !ok || c == nil {
p.Delete(key)
+167 -89
View File
@@ -1,7 +1,9 @@
package config
import (
"encoding/json"
"io"
"sync"
"time"
"github.com/spf13/viper"
@@ -19,149 +21,215 @@ 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 {
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]any `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]any `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]any `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]any `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"`
Interface string `yaml:",omitempty" json:"interface,omitempty"`
Admission string `yaml:",omitempty" json:"admission,omitempty"`
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
Handler *HandlerConfig `yaml:",omitempty" json:"handler,omitempty"`
Listener *ListenerConfig `yaml:",omitempty" json:"listener,omitempty"`
Forwarder *ForwarderConfig `yaml:",omitempty" json:"forwarder,omitempty"`
}
type ChainConfig struct {
Name string
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"`
Interface string `yaml:",omitempty" json:"interface,omitempty"`
Selector *SelectorConfig `yaml:",omitempty" json:"selector,omitempty"`
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
Nodes []*NodeConfig `json:"nodes"`
}
type NodeConfig struct {
Name string
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"`
Interface string `yaml:",omitempty" json:"interface,omitempty"`
Bypass string `yaml:",omitempty" json:"bypass,omitempty"`
Resolver string `yaml:",omitempty" json:"resolver,omitempty"`
Hosts string `yaml:",omitempty" json:"hosts,omitempty"`
Connector *ConnectorConfig `yaml:",omitempty" json:"connector,omitempty"`
Dialer *DialerConfig `yaml:",omitempty" json:"dialer,omitempty"`
}
type Config struct {
Services []*ServiceConfig
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 +256,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)
}
}
+136
View File
@@ -0,0 +1,136 @@
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]any{
"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]any{
"kind": "node",
"connector": v.Connector.Type,
"dialer": v.Dialer.Type,
"hop": hop.Name,
"node": v.Name,
})
connectorLogger := nodeLogger.WithFields(map[string]any{
"kind": "connector",
})
tlsCfg := v.Connector.TLS
if tlsCfg == nil {
tlsCfg = &config.TLSConfig{}
}
tlsConfig, err := tls_util.LoadClientConfig(
tlsCfg.CertFile, tlsCfg.KeyFile, tlsCfg.CAFile,
tlsCfg.Secure, tlsCfg.ServerName)
if err != nil {
chainLogger.Error(err)
return nil, err
}
cr := registry.ConnectorRegistry().Get(v.Connector.Type)(
connector.AuthOption(parseAuth(v.Connector.Auth)),
connector.TLSConfigOption(tlsConfig),
connector.LoggerOption(connectorLogger),
)
if v.Connector.Metadata == nil {
v.Connector.Metadata = make(map[string]any)
}
if err := cr.Init(metadata.MapMetadata(v.Connector.Metadata)); err != nil {
connectorLogger.Error("init: ", err)
return nil, err
}
dialerLogger := nodeLogger.WithFields(map[string]any{
"kind": "dialer",
})
tlsCfg = v.Dialer.TLS
if tlsCfg == nil {
tlsCfg = &config.TLSConfig{}
}
tlsConfig, err = tls_util.LoadClientConfig(
tlsCfg.CertFile, tlsCfg.KeyFile, tlsCfg.CAFile,
tlsCfg.Secure, tlsCfg.ServerName)
if err != nil {
chainLogger.Error(err)
return nil, err
}
d := registry.DialerRegistry().Get(v.Dialer.Type)(
dialer.AuthOption(parseAuth(v.Dialer.Auth)),
dialer.TLSConfigOption(tlsConfig),
dialer.LoggerOption(dialerLogger),
)
if v.Dialer.Metadata == nil {
v.Dialer.Metadata = make(map[string]any)
}
if err := d.Init(metadata.MapMetadata(v.Dialer.Metadata)); err != nil {
dialerLogger.Error("init: ", err)
return nil, err
}
if v.Bypass == "" {
v.Bypass = hop.Bypass
}
if v.Resolver == "" {
v.Resolver = hop.Resolver
}
if v.Hosts == "" {
v.Hosts = hop.Hosts
}
if v.Interface == "" {
v.Interface = hop.Interface
}
tr := (&chain.Transport{}).
WithConnector(cr).
WithDialer(d).
WithAddr(v.Addr).
WithInterface(v.Interface)
node := &chain.Node{
Name: v.Name,
Addr: v.Addr,
Bypass: registry.BypassRegistry().Get(v.Bypass),
Resolver: registry.ResolverRegistry().Get(v.Resolver),
Hosts: registry.HostsRegistry().Get(v.Hosts),
Marker: &chain.FailMarker{},
Transport: tr,
}
group.AddNode(node)
}
sel := selector
if s := parseSelector(hop.Selector); s != nil {
sel = s
}
group.WithSelector(sel)
c.AddNodeGroup(group)
}
return c, nil
}
+161
View File
@@ -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]any{
"kind": "admission",
"admission": cfg.Name,
})),
)
}
func ParseBypass(cfg *config.BypassConfig) bypass.Bypass {
if cfg == nil {
return nil
}
return bypass.NewBypassPatterns(
cfg.Reverse,
cfg.Matchers,
bypass.LoggerOption(logger.Default().WithFields(map[string]any{
"kind": "bypass",
"bypass": cfg.Name,
})),
)
}
func ParseResolver(cfg *config.ResolverConfig) (resolver.Resolver, error) {
if cfg == nil {
return nil, nil
}
var nameservers []resolver_impl.NameServer
for _, server := range cfg.Nameservers {
nameservers = append(nameservers, resolver_impl.NameServer{
Addr: server.Addr,
Chain: registry.ChainRegistry().Get(server.Chain),
TTL: server.TTL,
Timeout: server.Timeout,
ClientIP: net.ParseIP(server.ClientIP),
Prefer: server.Prefer,
Hostname: server.Hostname,
})
}
return resolver_impl.NewResolver(
nameservers,
resolver_impl.LoggerResolverOption(
logger.Default().WithFields(map[string]any{
"kind": "resolver",
"resolver": cfg.Name,
}),
),
)
}
func ParseHosts(cfg *config.HostsConfig) hostspkg.HostMapper {
if cfg == nil || len(cfg.Mappings) == 0 {
return nil
}
hosts := hostspkg.NewHosts()
hosts.Logger = logger.Default().WithFields(map[string]any{
"kind": "hosts",
"hosts": cfg.Name,
})
for _, host := range cfg.Mappings {
if host.IP == "" || host.Hostname == "" {
continue
}
ip := net.ParseIP(host.IP)
if ip == nil {
continue
}
hosts.Map(ip, host.Hostname, host.Aliases...)
}
return hosts
}
+148
View File
@@ -0,0 +1,148 @@
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]any{
"kind": "service",
"service": cfg.Name,
"listener": cfg.Listener.Type,
"handler": cfg.Handler.Type,
})
listenerLogger := serviceLogger.WithFields(map[string]any{
"kind": "listener",
})
tlsCfg := cfg.Listener.TLS
if tlsCfg == nil {
tlsCfg = &config.TLSConfig{}
}
tlsConfig, err := tls_util.LoadServerConfig(
tlsCfg.CertFile, tlsCfg.KeyFile, tlsCfg.CAFile)
if err != nil {
listenerLogger.Error(err)
return nil, err
}
auther := ParseAutherFromAuth(cfg.Listener.Auth)
if cfg.Listener.Auther != "" {
auther = registry.AutherRegistry().Get(cfg.Listener.Auther)
}
ln := registry.ListenerRegistry().Get(cfg.Listener.Type)(
listener.AddrOption(cfg.Addr),
listener.ChainOption(registry.ChainRegistry().Get(cfg.Listener.Chain)),
listener.AutherOption(auther),
listener.AuthOption(parseAuth(cfg.Listener.Auth)),
listener.TLSConfigOption(tlsConfig),
listener.LoggerOption(listenerLogger),
listener.ServiceOption(cfg.Name),
)
if cfg.Listener.Metadata == nil {
cfg.Listener.Metadata = make(map[string]any)
}
if err := ln.Init(metadata.MapMetadata(cfg.Listener.Metadata)); err != nil {
listenerLogger.Error("init: ", err)
return nil, err
}
handlerLogger := serviceLogger.WithFields(map[string]any{
"kind": "handler",
})
tlsCfg = cfg.Handler.TLS
if tlsCfg == nil {
tlsCfg = &config.TLSConfig{}
}
tlsConfig, err = tls_util.LoadServerConfig(
tlsCfg.CertFile, tlsCfg.KeyFile, tlsCfg.CAFile)
if err != nil {
handlerLogger.Error(err)
return nil, err
}
auther = ParseAutherFromAuth(cfg.Handler.Auth)
if cfg.Handler.Auther != "" {
auther = registry.AutherRegistry().Get(cfg.Handler.Auther)
}
router := (&chain.Router{}).
WithRetries(cfg.Handler.Retries).
// WithTimeout(timeout time.Duration).
WithInterface(cfg.Interface).
WithChain(registry.ChainRegistry().Get(cfg.Handler.Chain)).
WithResolver(registry.ResolverRegistry().Get(cfg.Resolver)).
WithHosts(registry.HostsRegistry().Get(cfg.Hosts)).
WithLogger(handlerLogger)
h := registry.HandlerRegistry().Get(cfg.Handler.Type)(
handler.RouterOption(router),
handler.AutherOption(auther),
handler.AuthOption(parseAuth(cfg.Handler.Auth)),
handler.BypassOption(registry.BypassRegistry().Get(cfg.Bypass)),
handler.TLSConfigOption(tlsConfig),
handler.LoggerOption(handlerLogger),
)
if forwarder, ok := h.(handler.Forwarder); ok {
forwarder.Forward(parseForwarder(cfg.Forwarder))
}
if cfg.Handler.Metadata == nil {
cfg.Handler.Metadata = make(map[string]any)
}
if err := h.Init(metadata.MapMetadata(cfg.Handler.Metadata)); err != nil {
handlerLogger.Error("init: ", err)
return nil, err
}
s := service.NewService(cfg.Name, ln, h,
service.AdmissionOption(registry.AdmissionRegistry().Get(cfg.Admission)),
service.LoggerOption(serviceLogger),
)
serviceLogger.Infof("listening on %s/%s", s.Addr().String(), s.Addr().Network())
return s, nil
}
func parseForwarder(cfg *config.ForwarderConfig) *chain.NodeGroup {
if cfg == nil || len(cfg.Targets) == 0 {
return nil
}
group := &chain.NodeGroup{}
for _, target := range cfg.Targets {
if v := strings.TrimSpace(target); v != "" {
group.AddNode(&chain.Node{
Name: target,
Addr: target,
Marker: &chain.FailMarker{},
})
}
}
return group.WithSelector(parseSelector(cfg.Selector))
}
+2 -2
View File
@@ -10,7 +10,7 @@ import (
)
func init() {
registry.RegiserConnector("forward", NewConnector)
registry.ConnectorRegistry().Register("forward", NewConnector)
}
type forwardConnector struct {
@@ -33,7 +33,7 @@ func (c *forwardConnector) Init(md md.Metadata) (err error) {
}
func (c *forwardConnector) Connect(ctx context.Context, conn net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
log := c.options.Logger.WithFields(map[string]interface{}{
log := c.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"network": network,
+3 -5
View File
@@ -19,11 +19,10 @@ import (
)
func init() {
registry.RegiserConnector("http", NewConnector)
registry.ConnectorRegistry().Register("http", NewConnector)
}
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,
}
}
@@ -45,7 +43,7 @@ func (c *httpConnector) Init(md md.Metadata) (err error) {
}
func (c *httpConnector) Connect(ctx context.Context, conn net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
log := c.options.Logger.WithFields(map[string]interface{}{
log := c.options.Logger.WithFields(map[string]any{
"local": conn.LocalAddr().String(),
"remote": conn.RemoteAddr().String(),
"network": network,
@@ -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",
+3 -5
View File
@@ -20,11 +20,10 @@ import (
)
func init() {
registry.RegiserConnector("http2", NewConnector)
registry.ConnectorRegistry().Register("http2", NewConnector)
}
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,
}
}
@@ -46,7 +44,7 @@ func (c *http2Connector) Init(md md.Metadata) (err error) {
}
func (c *http2Connector) Connect(ctx context.Context, conn net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
log := c.options.Logger.WithFields(map[string]interface{}{
log := c.options.Logger.WithFields(map[string]any{
"local": conn.LocalAddr().String(),
"remote": conn.RemoteAddr().String(),
"network": network,
@@ -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",
+3 -3
View File
@@ -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
}
}
+4 -4
View File
@@ -16,7 +16,7 @@ import (
// Bind implements connector.Binder.
func (c *relayConnector) Bind(ctx context.Context, conn net.Conn, network, address string, opts ...connector.BindOption) (net.Listener, error) {
log := c.options.Logger.WithFields(map[string]interface{}{
log := c.options.Logger.WithFields(map[string]any{
"network": network,
"address": address,
})
@@ -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,
})
}
+5 -8
View File
@@ -4,7 +4,6 @@ import (
"context"
"fmt"
"net"
"net/url"
"time"
"github.com/go-gost/gost/pkg/common/util/socks"
@@ -15,11 +14,10 @@ import (
)
func init() {
registry.RegiserConnector("relay", NewConnector)
registry.ConnectorRegistry().Register("relay", NewConnector)
}
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,
}
}
@@ -41,7 +38,7 @@ func (c *relayConnector) Init(md md.Metadata) (err error) {
}
func (c *relayConnector) Connect(ctx context.Context, conn net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
log := c.options.Logger.WithFields(map[string]interface{}{
log := c.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"network": network,
@@ -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,
})
}
+2 -2
View File
@@ -10,7 +10,7 @@ import (
)
func init() {
registry.RegiserConnector("sni", NewConnector)
registry.ConnectorRegistry().Register("sni", NewConnector)
}
type sniConnector struct {
@@ -34,7 +34,7 @@ func (c *sniConnector) Init(md md.Metadata) (err error) {
}
func (c *sniConnector) Connect(ctx context.Context, conn net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
log := c.options.Logger.WithFields(map[string]interface{}{
log := c.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"network": network,
+5 -8
View File
@@ -5,7 +5,6 @@ import (
"errors"
"fmt"
"net"
"net/url"
"strconv"
"time"
@@ -16,12 +15,11 @@ import (
)
func init() {
registry.RegiserConnector("socks4", NewConnector)
registry.RegiserConnector("socks4a", NewConnector)
registry.ConnectorRegistry().Register("socks4", NewConnector)
registry.ConnectorRegistry().Register("socks4a", NewConnector)
}
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,
}
}
@@ -43,7 +40,7 @@ func (c *socks4Connector) Init(md md.Metadata) (err error) {
}
func (c *socks4Connector) Connect(ctx context.Context, conn net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
log := c.options.Logger.WithFields(map[string]interface{}{
log := c.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"network": network,
@@ -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 {
+1 -1
View File
@@ -15,7 +15,7 @@ import (
// Bind implements connector.Binder.
func (c *socks5Connector) Bind(ctx context.Context, conn net.Conn, network, address string, opts ...connector.BindOption) (net.Listener, error) {
log := c.options.Logger.WithFields(map[string]interface{}{
log := c.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"network": network,
+5 -5
View File
@@ -17,8 +17,8 @@ import (
)
func init() {
registry.RegiserConnector("socks5", NewConnector)
registry.RegiserConnector("socks", NewConnector)
registry.ConnectorRegistry().Register("socks5", NewConnector)
registry.ConnectorRegistry().Register("socks", NewConnector)
}
type socks5Connector struct {
@@ -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,
}
@@ -67,7 +67,7 @@ func (c *socks5Connector) Init(md md.Metadata) (err error) {
// Handshake implements connector.Handshaker.
func (c *socks5Connector) Handshake(ctx context.Context, conn net.Conn) (net.Conn, error) {
log := c.options.Logger.WithFields(map[string]interface{}{
log := c.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
})
@@ -87,7 +87,7 @@ func (c *socks5Connector) Handshake(ctx context.Context, conn net.Conn) (net.Con
}
func (c *socks5Connector) Connect(ctx context.Context, conn net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
log := c.options.Logger.WithFields(map[string]interface{}{
log := c.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"network": network,
+5 -5
View File
@@ -16,7 +16,7 @@ import (
)
func init() {
registry.RegiserConnector("ss", NewConnector)
registry.ConnectorRegistry().Register("ss", NewConnector)
}
type ssConnector struct {
@@ -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)
}
@@ -51,7 +51,7 @@ func (c *ssConnector) Init(md md.Metadata) (err error) {
}
func (c *ssConnector) Connect(ctx context.Context, conn net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
log := c.options.Logger.WithFields(map[string]interface{}{
log := c.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"network": network,
+5 -5
View File
@@ -15,7 +15,7 @@ import (
)
func init() {
registry.RegiserConnector("ssu", NewConnector)
registry.ConnectorRegistry().Register("ssu", NewConnector)
}
type ssuConnector struct {
@@ -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)
}
@@ -50,7 +50,7 @@ func (c *ssuConnector) Init(md md.Metadata) (err error) {
}
func (c *ssuConnector) Connect(ctx context.Context, conn net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
log := c.options.Logger.WithFields(map[string]interface{}{
log := c.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"network": network,
+3 -3
View File
@@ -12,7 +12,7 @@ import (
)
func init() {
registry.RegiserConnector("sshd", NewConnector)
registry.ConnectorRegistry().Register("sshd", NewConnector)
}
type sshdConnector struct {
@@ -35,7 +35,7 @@ func (c *sshdConnector) Init(md md.Metadata) (err error) {
}
func (c *sshdConnector) Connect(ctx context.Context, conn net.Conn, network, address string, opts ...connector.ConnectOption) (net.Conn, error) {
log := c.options.Logger.WithFields(map[string]interface{}{
log := c.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"network": network,
@@ -59,7 +59,7 @@ func (c *sshdConnector) Connect(ctx context.Context, conn net.Conn, network, add
// Bind implements connector.Binder.
func (c *sshdConnector) Bind(ctx context.Context, conn net.Conn, network, address string, opts ...connector.BindOption) (net.Listener, error) {
log := c.options.Logger.WithFields(map[string]interface{}{
log := c.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
"network": network,
+1 -1
View File
@@ -12,7 +12,7 @@ import (
)
func init() {
registry.RegisterDialer("ftcp", NewDialer)
registry.DialerRegistry().Register("ftcp", NewDialer)
}
type ftcpDialer struct {
+8 -35
View File
@@ -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"
@@ -18,13 +17,12 @@ import (
)
func init() {
registry.RegisterDialer("grpc", NewDialer)
registry.DialerRegistry().Register("grpc", NewDialer)
}
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,17 +68,21 @@ 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)
return options.NetDialer.Dial(c, "tcp", s)
}),
grpc.WithAuthority(host),
grpc.WithConnectParams(grpc.ConnectParams{
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)
@@ -110,31 +111,3 @@ func (d *grpcDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
closed: make(chan struct{}),
}, nil
}
func (d *grpcDialer) dial(ctx context.Context, network, addr string, opts *dialer.DialOptions) (net.Conn, error) {
dial := opts.DialFunc
if dial != nil {
conn, err := dial(ctx, addr)
if err != nil {
d.logger.Error(err)
} else {
d.logger.WithFields(map[string]interface{}{
"src": conn.LocalAddr().String(),
"dst": addr,
}).Debug("dial with dial func")
}
return conn, err
}
var netd net.Dialer
conn, err := netd.DialContext(ctx, network, addr)
if err != nil {
d.logger.Error(err)
} else {
d.logger.WithFields(map[string]interface{}{
"src": conn.LocalAddr().String(),
"dst": addr,
}).Debugf("dial direct %s/%s", addr, network)
}
return conn, err
}
+7 -30
View File
@@ -7,6 +7,7 @@ import (
"sync"
"time"
net_dialer "github.com/go-gost/gost/pkg/common/net/dialer"
"github.com/go-gost/gost/pkg/dialer"
http2_util "github.com/go-gost/gost/pkg/internal/util/http2"
"github.com/go-gost/gost/pkg/logger"
@@ -15,7 +16,7 @@ import (
)
func init() {
registry.RegisterDialer("http2", NewDialer)
registry.DialerRegistry().Register("http2", NewDialer)
}
type http2Dialer struct {
@@ -73,7 +74,11 @@ func (d *http2Dialer) Dial(ctx context.Context, address string, opts ...dialer.D
Transport: &http.Transport{
TLSClientConfig: d.options.TLSConfig,
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return d.dial(ctx, network, addr, &options)
netd := options.NetDialer
if netd == nil {
netd = net_dialer.DefaultNetDialer
}
return netd.Dial(ctx, network, addr)
},
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
@@ -94,31 +99,3 @@ func (d *http2Dialer) Dial(ctx context.Context, address string, opts ...dialer.D
delete(d.clients, address)
}), nil
}
func (d *http2Dialer) dial(ctx context.Context, network, addr string, opts *dialer.DialOptions) (net.Conn, error) {
dial := opts.DialFunc
if dial != nil {
conn, err := dial(ctx, addr)
if err != nil {
d.logger.Error(err)
} else {
d.logger.WithFields(map[string]interface{}{
"src": conn.LocalAddr().String(),
"dst": addr,
}).Debug("dial with dial func")
}
return conn, err
}
var netd net.Dialer
conn, err := netd.DialContext(ctx, network, addr)
if err != nil {
d.logger.Error(err)
} else {
d.logger.WithFields(map[string]interface{}{
"src": conn.LocalAddr().String(),
"dst": addr,
}).Debugf("dial direct %s/%s", addr, network)
}
return conn, err
}
+4 -32
View File
@@ -20,8 +20,8 @@ import (
)
func init() {
registry.RegisterDialer("h2", NewTLSDialer)
registry.RegisterDialer("h2c", NewDialer)
registry.DialerRegistry().Register("h2", NewTLSDialer)
registry.DialerRegistry().Register("h2c", NewDialer)
}
type h2Dialer struct {
@@ -93,14 +93,14 @@ func (d *h2Dialer) Dial(ctx context.Context, address string, opts ...dialer.Dial
if d.h2c {
client.Transport = &http2.Transport{
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
return d.dial(ctx, network, addr, options)
return options.NetDialer.Dial(ctx, network, addr)
},
}
} else {
client.Transport = &http.Transport{
TLSClientConfig: d.options.TLSConfig,
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
return d.dial(ctx, network, addr, options)
return options.NetDialer.Dial(ctx, network, addr)
},
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
@@ -163,31 +163,3 @@ func (d *h2Dialer) Dial(ctx context.Context, address string, opts ...dialer.Dial
}
return conn, nil
}
func (d *h2Dialer) dial(ctx context.Context, network, addr string, opts *dialer.DialOptions) (net.Conn, error) {
dial := opts.DialFunc
if dial != nil {
conn, err := dial(ctx, addr)
if err != nil {
d.logger.Error(err)
} else {
d.logger.WithFields(map[string]interface{}{
"src": conn.LocalAddr().String(),
"dst": addr,
}).Debug("dial with dial func")
}
return conn, err
}
var netd net.Dialer
conn, err := netd.DialContext(ctx, network, addr)
if err != nil {
d.logger.Error(err)
} else {
d.logger.WithFields(map[string]interface{}{
"src": conn.LocalAddr().String(),
"dst": addr,
}).Debugf("dial direct %s/%s", addr, network)
}
return conn, err
}
+66 -23
View File
@@ -2,27 +2,29 @@ package http3
import (
"context"
"crypto/tls"
"net"
"net/http"
"sync"
"github.com/go-gost/gost/pkg/dialer"
pht_util "github.com/go-gost/gost/pkg/internal/util/pht"
"github.com/go-gost/gost/pkg/logger"
md "github.com/go-gost/gost/pkg/metadata"
"github.com/go-gost/gost/pkg/registry"
"github.com/lucas-clemente/quic-go"
"github.com/lucas-clemente/quic-go/http3"
)
func init() {
registry.RegisterDialer("http3", NewDialer)
registry.RegisterDialer("h3", NewDialer)
registry.DialerRegistry().Register("http3", NewDialer)
registry.DialerRegistry().Register("h3", NewDialer)
}
type http3Dialer struct {
client *pht_util.Client
md metadata
logger logger.Logger
options dialer.Options
clients map[string]*pht_util.Client
clientMutex sync.Mutex
md metadata
options dialer.Options
}
func NewDialer(opts ...dialer.Option) dialer.Dialer {
@@ -32,7 +34,7 @@ func NewDialer(opts ...dialer.Option) dialer.Dialer {
}
return &http3Dialer{
logger: options.Logger,
clients: make(map[string]*pht_util.Client),
options: options,
}
}
@@ -42,23 +44,64 @@ func (d *http3Dialer) Init(md md.Metadata) (err error) {
return
}
tr := &http3.RoundTripper{
TLSClientConfig: d.options.TLSConfig,
}
d.client = &pht_util.Client{
Client: &http.Client{
// Timeout: 60 * time.Second,
Transport: tr,
},
AuthorizePath: d.md.authorizePath,
PushPath: d.md.pushPath,
PullPath: d.md.pullPath,
TLSEnabled: true,
Logger: d.options.Logger,
}
return nil
}
func (d *http3Dialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOption) (net.Conn, error) {
return d.client.Dial(ctx, addr)
d.clientMutex.Lock()
defer d.clientMutex.Unlock()
client, ok := d.clients[addr]
if !ok {
var options dialer.DialOptions
for _, opt := range opts {
opt(&options)
}
host := d.md.host
if host == "" {
host = options.Host
}
if h, _, _ := net.SplitHostPort(host); h != "" {
host = h
}
client = &pht_util.Client{
Host: host,
Client: &http.Client{
// Timeout: 60 * time.Second,
Transport: &http3.RoundTripper{
TLSClientConfig: d.options.TLSConfig,
Dial: func(network, adr string, tlsCfg *tls.Config, cfg *quic.Config) (quic.EarlySession, error) {
// d.options.Logger.Infof("dial: %s/%s, %s", addr, network, host)
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
udpConn, err := options.NetDialer.Dial(context.Background(), "udp", "")
if err != nil {
return nil, err
}
return quic.DialEarly(udpConn.(net.PacketConn), udpAddr, host, tlsCfg, cfg)
},
},
},
AuthorizePath: d.md.authorizePath,
PushPath: d.md.pushPath,
PullPath: d.md.pullPath,
TLSEnabled: true,
Logger: d.options.Logger,
}
d.clients[addr] = client
}
return client.Dial(ctx, addr)
}
// Multiplex implements dialer.Multiplexer interface.
func (d *http3Dialer) Multiplex() bool {
return true
}
+4
View File
@@ -23,6 +23,7 @@ type metadata struct {
authorizePath string
pushPath string
pullPath string
host string
}
func (d *http3Dialer) parseMetadata(md mdata.Metadata) (err error) {
@@ -30,6 +31,7 @@ func (d *http3Dialer) parseMetadata(md mdata.Metadata) (err error) {
authorizePath = "authorizePath"
pushPath = "pushPath"
pullPath = "pullPath"
host = "host"
)
d.md.authorizePath = mdata.GetString(md, authorizePath)
@@ -44,5 +46,7 @@ func (d *http3Dialer) parseMetadata(md mdata.Metadata) (err error) {
if !strings.HasPrefix(d.md.pullPath, "/") {
d.md.pullPath = defaultPullPath
}
d.md.host = mdata.GetString(md, host)
return
}
+42
View File
@@ -0,0 +1,42 @@
package quic
import (
"context"
"net"
"github.com/lucas-clemente/quic-go"
)
type quicSession struct {
session quic.Session
}
func (session *quicSession) GetConn() (*quicConn, error) {
stream, err := session.session.OpenStreamSync(context.Background())
if err != nil {
return nil, err
}
return &quicConn{
Stream: stream,
laddr: session.session.LocalAddr(),
raddr: session.session.RemoteAddr(),
}, nil
}
func (session *quicSession) Close() error {
return session.session.CloseWithError(quic.ApplicationErrorCode(0), "closed")
}
type quicConn struct {
quic.Stream
laddr net.Addr
raddr net.Addr
}
func (c *quicConn) LocalAddr() net.Addr {
return c.laddr
}
func (c *quicConn) RemoteAddr() net.Addr {
return c.raddr
}
+130
View File
@@ -0,0 +1,130 @@
package quic
import (
"context"
"math"
"math/rand"
"net"
"sync"
"time"
"github.com/go-gost/gost/pkg/dialer"
icmp_pkg "github.com/go-gost/gost/pkg/internal/util/icmp"
"github.com/go-gost/gost/pkg/logger"
md "github.com/go-gost/gost/pkg/metadata"
"github.com/go-gost/gost/pkg/registry"
"github.com/lucas-clemente/quic-go"
"golang.org/x/net/icmp"
)
func init() {
registry.DialerRegistry().Register("icmp", NewDialer)
}
type icmpDialer struct {
sessions map[string]*quicSession
sessionMutex sync.Mutex
logger logger.Logger
md metadata
options dialer.Options
}
func NewDialer(opts ...dialer.Option) dialer.Dialer {
options := dialer.Options{}
for _, opt := range opts {
opt(&options)
}
return &icmpDialer{
sessions: make(map[string]*quicSession),
logger: options.Logger,
options: options,
}
}
func (d *icmpDialer) Init(md md.Metadata) (err error) {
if err = d.parseMetadata(md); err != nil {
return
}
return nil
}
func (d *icmpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOption) (conn net.Conn, err error) {
if _, _, err := net.SplitHostPort(addr); err != nil {
addr = net.JoinHostPort(addr, "0")
}
raddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
d.sessionMutex.Lock()
defer d.sessionMutex.Unlock()
session, ok := d.sessions[addr]
if !ok {
options := &dialer.DialOptions{}
for _, opt := range opts {
opt(options)
}
var pc net.PacketConn
pc, err = icmp.ListenPacket("ip4:icmp", "")
if err != nil {
return
}
id := raddr.Port
if id == 0 {
id = rand.New(rand.NewSource(time.Now().UnixNano())).Intn(math.MaxUint16) + 1
raddr.Port = id
}
pc = icmp_pkg.ClientConn(pc, id)
session, err = d.initSession(ctx, raddr, pc)
if err != nil {
d.logger.Error(err)
pc.Close()
return nil, err
}
d.sessions[addr] = session
}
conn, err = session.GetConn()
if err != nil {
session.Close()
delete(d.sessions, addr)
return nil, err
}
return
}
func (d *icmpDialer) initSession(ctx context.Context, addr net.Addr, conn net.PacketConn) (*quicSession, error) {
quicConfig := &quic.Config{
KeepAlive: d.md.keepAlive,
HandshakeIdleTimeout: d.md.handshakeTimeout,
MaxIdleTimeout: d.md.maxIdleTimeout,
Versions: []quic.VersionNumber{
quic.Version1,
quic.VersionDraft29,
},
}
tlsCfg := d.options.TLSConfig
tlsCfg.NextProtos = []string{"http/3", "quic/v1"}
session, err := quic.DialContext(ctx, conn, addr, addr.String(), tlsCfg, quicConfig)
if err != nil {
return nil, err
}
return &quicSession{session: session}, nil
}
// Multiplex implements dialer.Multiplexer interface.
func (d *icmpDialer) Multiplex() bool {
return true
}
+29
View File
@@ -0,0 +1,29 @@
package quic
import (
"time"
mdata "github.com/go-gost/gost/pkg/metadata"
)
type metadata struct {
keepAlive bool
maxIdleTimeout time.Duration
handshakeTimeout time.Duration
}
func (d *icmpDialer) parseMetadata(md mdata.Metadata) (err error) {
const (
keepAlive = "keepAlive"
handshakeTimeout = "handshakeTimeout"
maxIdleTimeout = "maxIdleTimeout"
)
d.md.handshakeTimeout = mdata.GetDuration(md, handshakeTimeout)
d.md.keepAlive = mdata.GetBool(md, keepAlive)
d.md.handshakeTimeout = mdata.GetDuration(md, handshakeTimeout)
d.md.maxIdleTimeout = mdata.GetDuration(md, maxIdleTimeout)
return
}
-1
View File
@@ -7,7 +7,6 @@ import (
)
type muxSession struct {
conn net.Conn
session *smux.Session
}
+47 -71
View File
@@ -18,7 +18,7 @@ import (
)
func init() {
registry.RegisterDialer("kcp", NewDialer)
registry.DialerRegistry().Register("kcp", NewDialer)
}
type kcpDialer struct {
@@ -26,17 +26,19 @@ type kcpDialer struct {
sessionMutex sync.Mutex
logger logger.Logger
md metadata
options dialer.Options
}
func NewDialer(opts ...dialer.Option) dialer.Dialer {
options := &dialer.Options{}
options := dialer.Options{}
for _, opt := range opts {
opt(options)
opt(&options)
}
return &kcpDialer{
sessions: make(map[string]*muxSession),
logger: options.Logger,
options: options,
}
}
@@ -50,15 +52,10 @@ func (d *kcpDialer) Init(md md.Metadata) (err error) {
return nil
}
// Multiplex implements dialer.Multiplexer interface.
func (d *kcpDialer) Multiplex() bool {
return true
}
func (d *kcpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOption) (conn net.Conn, err error) {
var options dialer.DialOptions
for _, opt := range opts {
opt(&options)
raddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
d.sessionMutex.Lock()
@@ -70,86 +67,60 @@ func (d *kcpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp
ok = false
}
if !ok {
raddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
var options dialer.DialOptions
for _, opt := range opts {
opt(&options)
}
var pc net.PacketConn
if d.md.config.TCP {
pc, err := tcpraw.Dial("tcp", addr)
pc, err = tcpraw.Dial("tcp", addr)
if err != nil {
return nil, err
}
conn = &fakeTCPConn{
pc = &fakeTCPConn{
raddr: raddr,
PacketConn: pc,
}
} else {
conn, err = net.ListenUDP("udp", nil)
c, err := options.NetDialer.Dial(ctx, "udp", addr)
if err != nil {
return nil, err
}
var ok bool
pc, ok = c.(net.PacketConn)
if !ok {
c.Close()
return nil, errors.New("quic: wrong connection type")
}
}
session, err = d.initSession(ctx, raddr, pc)
if err != nil {
d.logger.Error(err)
pc.Close()
return nil, err
}
session = &muxSession{conn: conn}
d.sessions[addr] = session
}
return session.conn, err
}
// Handshake implements dialer.Handshaker
func (d *kcpDialer) Handshake(ctx context.Context, conn net.Conn, options ...dialer.HandshakeOption) (net.Conn, error) {
opts := &dialer.HandshakeOptions{}
for _, option := range options {
option(opts)
}
d.sessionMutex.Lock()
defer d.sessionMutex.Unlock()
if d.md.handshakeTimeout > 0 {
conn.SetDeadline(time.Now().Add(d.md.handshakeTimeout))
defer conn.SetDeadline(time.Time{})
}
session, ok := d.sessions[opts.Addr]
if session != nil && session.conn != conn {
conn.Close()
return nil, errors.New("kcp: unrecognized connection")
}
if !ok || session.session == nil {
s, err := d.initSession(ctx, opts.Addr, conn)
if err != nil {
d.logger.Error(err)
conn.Close()
delete(d.sessions, opts.Addr)
return nil, err
}
session = s
d.sessions[opts.Addr] = session
}
cc, err := session.GetConn()
conn, err = session.GetConn()
if err != nil {
session.Close()
delete(d.sessions, opts.Addr)
delete(d.sessions, addr)
return nil, err
}
return cc, nil
return
}
func (d *kcpDialer) initSession(ctx context.Context, addr string, conn net.Conn) (*muxSession, error) {
pc, ok := conn.(net.PacketConn)
if !ok {
return nil, errors.New("kcp: wrong connection type")
}
func (d *kcpDialer) initSession(ctx context.Context, addr net.Addr, conn net.PacketConn) (*muxSession, error) {
config := d.md.config
kcpconn, err := kcp.NewConn(addr,
kcpconn, err := kcp.NewConn(addr.String(),
kcp_util.BlockCrypt(config.Key, config.Crypt, kcp_util.DefaultSalt),
config.DataShard, config.ParityShard, pc)
config.DataShard, config.ParityShard, conn)
if err != nil {
return nil, err
}
@@ -162,15 +133,15 @@ func (d *kcpDialer) initSession(ctx context.Context, addr string, conn net.Conn)
kcpconn.SetACKNoDelay(config.AckNodelay)
if config.DSCP > 0 {
if err := kcpconn.SetDSCP(config.DSCP); err != nil {
d.logger.Warn("SetDSCP: ", err)
if er := kcpconn.SetDSCP(config.DSCP); er != nil {
d.logger.Warn("SetDSCP: ", er)
}
}
if err := kcpconn.SetReadBuffer(config.SockBuf); err != nil {
d.logger.Warn("SetReadBuffer: ", err)
if er := kcpconn.SetReadBuffer(config.SockBuf); er != nil {
d.logger.Warn("SetReadBuffer: ", er)
}
if err := kcpconn.SetWriteBuffer(config.SockBuf); err != nil {
d.logger.Warn("SetWriteBuffer: ", err)
if er := kcpconn.SetWriteBuffer(config.SockBuf); er != nil {
d.logger.Warn("SetWriteBuffer: ", er)
}
// stream multiplex
@@ -185,5 +156,10 @@ func (d *kcpDialer) initSession(ctx context.Context, addr string, conn net.Conn)
if err != nil {
return nil, err
}
return &muxSession{conn: conn, session: session}, nil
return &muxSession{session: session}, nil
}
// Multiplex implements dialer.Multiplexer interface.
func (d *kcpDialer) Multiplex() bool {
return true
}
+7 -3
View File
@@ -11,7 +11,7 @@ import (
)
func init() {
registry.RegisterDialer("ohttp", NewDialer)
registry.DialerRegistry().Register("ohttp", NewDialer)
}
type obfsHTTPDialer struct {
@@ -35,8 +35,12 @@ func (d *obfsHTTPDialer) Init(md md.Metadata) (err error) {
}
func (d *obfsHTTPDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOption) (net.Conn, error) {
var netd net.Dialer
conn, err := netd.DialContext(ctx, "tcp", addr)
options := &dialer.DialOptions{}
for _, opt := range opts {
opt(options)
}
conn, err := options.NetDialer.Dial(ctx, "tcp", addr)
if err != nil {
d.logger.Error(err)
}
+7 -3
View File
@@ -11,7 +11,7 @@ import (
)
func init() {
registry.RegisterDialer("otls", NewDialer)
registry.DialerRegistry().Register("otls", NewDialer)
}
type obfsTLSDialer struct {
@@ -35,8 +35,12 @@ func (d *obfsTLSDialer) Init(md md.Metadata) (err error) {
}
func (d *obfsTLSDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOption) (net.Conn, error) {
var netd net.Dialer
conn, err := netd.DialContext(ctx, "tcp", addr)
options := &dialer.DialOptions{}
for _, opt := range opts {
opt(options)
}
conn, err := options.NetDialer.Dial(ctx, "tcp", addr)
if err != nil {
d.logger.Error(err)
}
+8 -9
View File
@@ -1,25 +1,24 @@
package dialer
import (
"context"
"crypto/tls"
"net"
"net/url"
"github.com/go-gost/gost/pkg/common/net/dialer"
"github.com/go-gost/gost/pkg/logger"
)
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
}
}
@@ -36,8 +35,8 @@ func LoggerOption(logger logger.Logger) Option {
}
type DialOptions struct {
Host string
DialFunc func(ctx context.Context, addr string) (net.Conn, error)
Host string
NetDialer *dialer.NetDialer
}
type DialOption func(opts *DialOptions)
@@ -48,9 +47,9 @@ func HostDialOption(host string) DialOption {
}
}
func DialFuncDialOption(dialf func(ctx context.Context, addr string) (net.Conn, error)) DialOption {
func NetDialerDialOption(netd *dialer.NetDialer) DialOption {
return func(opts *DialOptions) {
opts.DialFunc = dialf
opts.NetDialer = netd
}
}
+60 -37
View File
@@ -4,6 +4,7 @@ import (
"context"
"net"
"net/http"
"sync"
"time"
"github.com/go-gost/gost/pkg/dialer"
@@ -14,16 +15,17 @@ import (
)
func init() {
registry.RegisterDialer("pht", NewDialer)
registry.RegisterDialer("phts", NewTLSDialer)
registry.DialerRegistry().Register("pht", NewDialer)
registry.DialerRegistry().Register("phts", NewTLSDialer)
}
type phtDialer struct {
tlsEnabled bool
client *pht_util.Client
md metadata
logger logger.Logger
options dialer.Options
clients map[string]*pht_util.Client
clientMutex sync.Mutex
tlsEnabled bool
md metadata
logger logger.Logger
options dialer.Options
}
func NewDialer(opts ...dialer.Option) dialer.Dialer {
@@ -33,7 +35,7 @@ func NewDialer(opts ...dialer.Option) dialer.Dialer {
}
return &phtDialer{
logger: options.Logger,
clients: make(map[string]*pht_util.Client),
options: options,
}
}
@@ -46,7 +48,7 @@ func NewTLSDialer(opts ...dialer.Option) dialer.Dialer {
return &phtDialer{
tlsEnabled: true,
logger: options.Logger,
clients: make(map[string]*pht_util.Client),
options: options,
}
}
@@ -56,36 +58,57 @@ func (d *phtDialer) Init(md md.Metadata) (err error) {
return
}
tr := &http.Transport{
// Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
if d.tlsEnabled {
tr.TLSClientConfig = d.options.TLSConfig
}
d.client = &pht_util.Client{
Client: &http.Client{
// Timeout: 60 * time.Second,
Transport: tr,
},
AuthorizePath: d.md.authorizePath,
PushPath: d.md.pushPath,
PullPath: d.md.pullPath,
TLSEnabled: d.tlsEnabled,
Logger: d.options.Logger,
}
return nil
}
func (d *phtDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOption) (net.Conn, error) {
return d.client.Dial(ctx, addr)
d.clientMutex.Lock()
defer d.clientMutex.Unlock()
client, ok := d.clients[addr]
if !ok {
var options dialer.DialOptions
for _, opt := range opts {
opt(&options)
}
host := d.md.host
if host == "" {
host = options.Host
}
if h, _, _ := net.SplitHostPort(host); h != "" {
host = h
}
tr := &http.Transport{
// Proxy: http.ProxyFromEnvironment,
DialContext: func(ctx context.Context, network, adr string) (net.Conn, error) {
return options.NetDialer.Dial(ctx, network, addr)
},
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
if d.tlsEnabled {
tr.TLSClientConfig = d.options.TLSConfig
}
client = &pht_util.Client{
Host: host,
Client: &http.Client{
// Timeout: 60 * time.Second,
Transport: tr,
},
AuthorizePath: d.md.authorizePath,
PushPath: d.md.pushPath,
PullPath: d.md.pullPath,
TLSEnabled: d.tlsEnabled,
Logger: d.options.Logger,
}
d.clients[addr] = client
}
return client.Dial(ctx, addr)
}
+4
View File
@@ -23,6 +23,7 @@ type metadata struct {
authorizePath string
pushPath string
pullPath string
host string
}
func (d *phtDialer) parseMetadata(md mdata.Metadata) (err error) {
@@ -30,6 +31,7 @@ func (d *phtDialer) parseMetadata(md mdata.Metadata) (err error) {
authorizePath = "authorizePath"
pushPath = "pushPath"
pullPath = "pullPath"
host = "host"
)
d.md.authorizePath = mdata.GetString(md, authorizePath)
@@ -44,5 +46,7 @@ func (d *phtDialer) parseMetadata(md mdata.Metadata) (err error) {
if !strings.HasPrefix(d.md.pullPath, "/") {
d.md.pullPath = defaultPullPath
}
d.md.host = mdata.GetString(md, host)
return
}
-1
View File
@@ -8,7 +8,6 @@ import (
)
type quicSession struct {
conn net.Conn
session quic.Session
}
+39 -66
View File
@@ -5,7 +5,6 @@ import (
"errors"
"net"
"sync"
"time"
"github.com/go-gost/gost/pkg/dialer"
quic_util "github.com/go-gost/gost/pkg/internal/util/quic"
@@ -16,7 +15,7 @@ import (
)
func init() {
registry.RegisterDialer("quic", NewDialer)
registry.DialerRegistry().Register("quic", NewDialer)
}
type quicDialer struct {
@@ -48,15 +47,14 @@ func (d *quicDialer) Init(md md.Metadata) (err error) {
return nil
}
// Multiplex implements dialer.Multiplexer interface.
func (d *quicDialer) Multiplex() bool {
return true
}
func (d *quicDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOption) (conn net.Conn, err error) {
var options dialer.DialOptions
for _, opt := range opts {
opt(&options)
if _, _, err := net.SplitHostPort(addr); err != nil {
addr = net.JoinHostPort(addr, "0")
}
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
d.sessionMutex.Lock()
@@ -64,75 +62,46 @@ func (d *quicDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
session, ok := d.sessions[addr]
if !ok {
var cc *net.UDPConn
cc, err = net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 0})
options := &dialer.DialOptions{}
for _, opt := range opts {
opt(options)
}
c, err := options.NetDialer.Dial(ctx, "udp", "")
if err != nil {
return
return nil, err
}
pc, ok := c.(net.PacketConn)
if !ok {
c.Close()
return nil, errors.New("quic: wrong connection type")
}
conn = cc
if d.md.cipherKey != nil {
conn = quic_util.CipherConn(cc, d.md.cipherKey)
pc = quic_util.CipherPacketConn(pc, d.md.cipherKey)
}
session, err = d.initSession(ctx, udpAddr, pc)
if err != nil {
d.logger.Error(err)
pc.Close()
return nil, err
}
session = &quicSession{conn: conn}
d.sessions[addr] = session
}
return session.conn, err
}
// Handshake implements dialer.Handshaker
func (d *quicDialer) Handshake(ctx context.Context, conn net.Conn, options ...dialer.HandshakeOption) (net.Conn, error) {
opts := &dialer.HandshakeOptions{}
for _, option := range options {
option(opts)
}
d.sessionMutex.Lock()
defer d.sessionMutex.Unlock()
if d.md.handshakeTimeout > 0 {
conn.SetDeadline(time.Now().Add(d.md.handshakeTimeout))
defer conn.SetDeadline(time.Time{})
}
session, ok := d.sessions[opts.Addr]
if session != nil && session.conn != conn {
conn.Close()
return nil, errors.New("quic: unrecognized connection")
}
if !ok || session.session == nil {
s, err := d.initSession(ctx, opts.Addr, conn)
if err != nil {
d.logger.Error(err)
conn.Close()
delete(d.sessions, opts.Addr)
return nil, err
}
session = s
d.sessions[opts.Addr] = session
}
cc, err := session.GetConn()
conn, err = session.GetConn()
if err != nil {
session.Close()
delete(d.sessions, opts.Addr)
delete(d.sessions, addr)
return nil, err
}
return cc, nil
return
}
func (d *quicDialer) initSession(ctx context.Context, addr string, conn net.Conn) (*quicSession, error) {
pc, ok := conn.(net.PacketConn)
if !ok {
return nil, errors.New("quic: wrong connection type")
}
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
func (d *quicDialer) initSession(ctx context.Context, addr net.Addr, conn net.PacketConn) (*quicSession, error) {
quicConfig := &quic.Config{
KeepAlive: d.md.keepAlive,
HandshakeIdleTimeout: d.md.handshakeTimeout,
@@ -146,10 +115,14 @@ func (d *quicDialer) initSession(ctx context.Context, addr string, conn net.Conn
tlsCfg := d.options.TLSConfig
tlsCfg.NextProtos = []string{"http/3", "quic/v1"}
session, err := quic.DialContext(ctx, pc, udpAddr, addr, tlsCfg, quicConfig)
session, err := quic.DialContext(ctx, conn, addr, addr.String(), tlsCfg, quicConfig)
if err != nil {
d.logger.Error(err)
return nil, err
}
return &quicSession{conn: conn, session: session}, nil
return &quicSession{session: session}, nil
}
// Multiplex implements dialer.Multiplexer interface.
func (d *quicDialer) Multiplex() bool {
return true
}
+4
View File
@@ -12,6 +12,7 @@ type metadata struct {
handshakeTimeout time.Duration
cipherKey []byte
host string
}
func (d *quicDialer) parseMetadata(md mdata.Metadata) (err error) {
@@ -21,6 +22,7 @@ func (d *quicDialer) parseMetadata(md mdata.Metadata) (err error) {
maxIdleTimeout = "maxIdleTimeout"
cipherKey = "cipherKey"
host = "host"
)
d.md.handshakeTimeout = mdata.GetDuration(md, handshakeTimeout)
@@ -32,5 +34,7 @@ func (d *quicDialer) parseMetadata(md mdata.Metadata) (err error) {
d.md.keepAlive = mdata.GetBool(md, keepAlive)
d.md.handshakeTimeout = mdata.GetDuration(md, handshakeTimeout)
d.md.maxIdleTimeout = mdata.GetDuration(md, maxIdleTimeout)
d.md.host = mdata.GetString(md, host)
return
}
+7 -35
View File
@@ -16,7 +16,7 @@ import (
)
func init() {
registry.RegisterDialer("ssh", NewDialer)
registry.DialerRegistry().Register("ssh", NewDialer)
}
type sshDialer struct {
@@ -52,11 +52,6 @@ func (d *sshDialer) Multiplex() bool {
}
func (d *sshDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOption) (conn net.Conn, err error) {
var options dialer.DialOptions
for _, opt := range opts {
opt(&options)
}
d.sessionMutex.Lock()
defer d.sessionMutex.Unlock()
@@ -66,7 +61,12 @@ func (d *sshDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp
ok = false
}
if !ok {
conn, err = d.dial(ctx, "tcp", addr, &options)
var options dialer.DialOptions
for _, opt := range opts {
opt(&options)
}
conn, err = options.NetDialer.Dial(ctx, "tcp", addr)
if err != nil {
return
}
@@ -134,34 +134,6 @@ func (d *sshDialer) Handshake(ctx context.Context, conn net.Conn, options ...dia
return ssh_util.NewConn(conn, channel), nil
}
func (d *sshDialer) dial(ctx context.Context, network, addr string, opts *dialer.DialOptions) (net.Conn, error) {
dial := opts.DialFunc
if dial != nil {
conn, err := dial(ctx, addr)
if err != nil {
d.logger.Error(err)
} else {
d.logger.WithFields(map[string]interface{}{
"src": conn.LocalAddr().String(),
"dst": addr,
}).Debug("dial with dial func")
}
return conn, err
}
var netd net.Dialer
conn, err := netd.DialContext(ctx, network, addr)
if err != nil {
d.logger.Error(err)
} else {
d.logger.WithFields(map[string]interface{}{
"src": conn.LocalAddr().String(),
"dst": addr,
}).Debugf("dial direct %s/%s", addr, network)
}
return conn, err
}
func (d *sshDialer) initSession(ctx context.Context, addr string, conn net.Conn) (*sshSession, error) {
config := ssh.ClientConfig{
Timeout: 30 * time.Second,
+10 -43
View File
@@ -4,7 +4,6 @@ import (
"context"
"errors"
"net"
"net/url"
"sync"
"time"
@@ -16,11 +15,10 @@ import (
)
func init() {
registry.RegisterDialer("sshd", NewDialer)
registry.DialerRegistry().Register("sshd", NewDialer)
}
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,
}
@@ -54,11 +51,6 @@ func (d *sshdDialer) Multiplex() bool {
}
func (d *sshdDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOption) (conn net.Conn, err error) {
var options dialer.DialOptions
for _, opt := range opts {
opt(&options)
}
d.sessionMutex.Lock()
defer d.sessionMutex.Unlock()
@@ -68,7 +60,12 @@ func (d *sshdDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
ok = false
}
if !ok {
conn, err = d.dial(ctx, "tcp", addr, &options)
var options dialer.DialOptions
for _, opt := range opts {
opt(&options)
}
conn, err = options.NetDialer.Dial(ctx, "tcp", addr)
if err != nil {
return
}
@@ -132,44 +129,14 @@ func (d *sshdDialer) Handshake(ctx context.Context, conn net.Conn, options ...di
return ssh_util.NewClientConn(session.conn, session.client), nil
}
func (d *sshdDialer) dial(ctx context.Context, network, addr string, opts *dialer.DialOptions) (net.Conn, error) {
log := d.options.Logger
dial := opts.DialFunc
if dial != nil {
conn, err := dial(ctx, addr)
if err != nil {
log.Error(err)
} else {
log.WithFields(map[string]interface{}{
"src": conn.LocalAddr().String(),
"dst": addr,
}).Debug("dial with dial func")
}
return conn, err
}
var netd net.Dialer
conn, err := netd.DialContext(ctx, network, addr)
if err != nil {
log.Error(err)
} else {
log.WithFields(map[string]interface{}{
"src": conn.LocalAddr().String(),
"dst": addr,
}).Debugf("dial direct %s/%s", addr, network)
}
return conn, err
}
func (d *sshdDialer) initSession(ctx context.Context, addr string, conn net.Conn) (*sshSession, error) {
config := ssh.ClientConfig{
// 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),
}
+2 -3
View File
@@ -11,7 +11,7 @@ import (
)
func init() {
registry.RegisterDialer("tcp", NewDialer)
registry.DialerRegistry().Register("tcp", NewDialer)
}
type tcpDialer struct {
@@ -40,8 +40,7 @@ func (d *tcpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp
opt(&options)
}
var netd net.Dialer
conn, err := netd.DialContext(ctx, "tcp", addr)
conn, err := options.NetDialer.Dial(ctx, "tcp", addr)
if err != nil {
d.logger.Error(err)
}
+2 -3
View File
@@ -13,7 +13,7 @@ import (
)
func init() {
registry.RegisterDialer("tls", NewDialer)
registry.DialerRegistry().Register("tls", NewDialer)
}
type tlsDialer struct {
@@ -44,8 +44,7 @@ func (d *tlsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp
opt(&options)
}
var netd net.Dialer
conn, err := netd.DialContext(ctx, "tcp", addr)
conn, err := options.NetDialer.Dial(ctx, "tcp", addr)
if err != nil {
d.logger.Error(err)
}
+7 -35
View File
@@ -16,7 +16,7 @@ import (
)
func init() {
registry.RegisterDialer("mtls", NewDialer)
registry.DialerRegistry().Register("mtls", NewDialer)
}
type mtlsDialer struct {
@@ -54,11 +54,6 @@ func (d *mtlsDialer) Multiplex() bool {
}
func (d *mtlsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOption) (conn net.Conn, err error) {
var options dialer.DialOptions
for _, opt := range opts {
opt(&options)
}
d.sessionMutex.Lock()
defer d.sessionMutex.Unlock()
@@ -68,7 +63,12 @@ func (d *mtlsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialO
ok = false
}
if !ok {
conn, err = d.dial(ctx, "tcp", addr, &options)
var options dialer.DialOptions
for _, opt := range opts {
opt(&options)
}
conn, err = options.NetDialer.Dial(ctx, "tcp", addr)
if err != nil {
return
}
@@ -122,34 +122,6 @@ func (d *mtlsDialer) Handshake(ctx context.Context, conn net.Conn, options ...di
return cc, nil
}
func (d *mtlsDialer) dial(ctx context.Context, network, addr string, opts *dialer.DialOptions) (net.Conn, error) {
dial := opts.DialFunc
if dial != nil {
conn, err := dial(ctx, addr)
if err != nil {
d.logger.Error(err)
} else {
d.logger.WithFields(map[string]interface{}{
"src": conn.LocalAddr().String(),
"dst": addr,
}).Debug("dial with dial func")
}
return conn, err
}
var netd net.Dialer
conn, err := netd.DialContext(ctx, network, addr)
if err != nil {
d.logger.Error(err)
} else {
d.logger.WithFields(map[string]interface{}{
"src": conn.LocalAddr().String(),
"dst": addr,
}).Debugf("dial direct %s/%s", addr, network)
}
return conn, err
}
func (d *mtlsDialer) initSession(ctx context.Context, conn net.Conn) (*muxSession, error) {
tlsConn := tls.Client(conn, d.options.TLSConfig)
if err := tlsConn.HandshakeContext(ctx); err != nil {
+6 -6
View File
@@ -11,7 +11,7 @@ import (
)
func init() {
registry.RegisterDialer("udp", NewDialer)
registry.DialerRegistry().Register("udp", NewDialer)
}
type udpDialer struct {
@@ -35,16 +35,16 @@ func (d *udpDialer) Init(md md.Metadata) (err error) {
}
func (d *udpDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOption) (net.Conn, error) {
taddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
var options dialer.DialOptions
for _, opt := range opts {
opt(&options)
}
c, err := net.DialUDP("udp", nil, taddr)
c, err := options.NetDialer.Dial(ctx, "udp", addr)
if err != nil {
return nil, err
}
return &conn{
UDPConn: c,
UDPConn: c.(*net.UDPConn),
}, nil
}
+33 -13
View File
@@ -8,20 +8,18 @@ import (
"github.com/go-gost/gost/pkg/dialer"
ws_util "github.com/go-gost/gost/pkg/internal/util/ws"
"github.com/go-gost/gost/pkg/logger"
md "github.com/go-gost/gost/pkg/metadata"
"github.com/go-gost/gost/pkg/registry"
"github.com/gorilla/websocket"
)
func init() {
registry.RegisterDialer("ws", NewDialer)
registry.RegisterDialer("wss", NewTLSDialer)
registry.DialerRegistry().Register("ws", NewDialer)
registry.DialerRegistry().Register("wss", NewTLSDialer)
}
type wsDialer struct {
tlsEnabled bool
logger logger.Logger
md metadata
options dialer.Options
}
@@ -33,7 +31,6 @@ func NewDialer(opts ...dialer.Option) dialer.Dialer {
}
return &wsDialer{
logger: options.Logger,
options: options,
}
}
@@ -46,7 +43,6 @@ func NewTLSDialer(opts ...dialer.Option) dialer.Dialer {
return &wsDialer{
tlsEnabled: true,
logger: options.Logger,
options: options,
}
}
@@ -61,10 +57,9 @@ func (d *wsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOpt
opt(&options)
}
var netd net.Dialer
conn, err := netd.DialContext(ctx, "tcp", addr)
conn, err := options.NetDialer.Dial(ctx, "tcp", addr)
if err != nil {
d.logger.Error(err)
d.options.Logger.Error(err)
}
return conn, err
}
@@ -77,8 +72,8 @@ func (d *wsDialer) Handshake(ctx context.Context, conn net.Conn, options ...dial
}
if d.md.handshakeTimeout > 0 {
conn.SetDeadline(time.Now().Add(d.md.handshakeTimeout))
defer conn.SetDeadline(time.Time{})
conn.SetReadDeadline(time.Now().Add(d.md.handshakeTimeout))
defer conn.SetReadDeadline(time.Time{})
}
host := d.md.host
@@ -102,11 +97,36 @@ func (d *wsDialer) Handshake(ctx context.Context, conn net.Conn, options ...dial
dialer.TLSClientConfig = d.options.TLSConfig
}
c, resp, err := dialer.Dial(url.String(), d.md.header)
c, resp, err := dialer.DialContext(ctx, url.String(), d.md.header)
if err != nil {
return nil, err
}
resp.Body.Close()
return ws_util.Conn(c), nil
cc := ws_util.Conn(c)
if d.md.keepAlive > 0 {
c.SetReadDeadline(time.Now().Add(d.md.keepAlive * 2))
c.SetPongHandler(func(string) error {
c.SetReadDeadline(time.Now().Add(d.md.keepAlive * 2))
d.options.Logger.Infof("pong: set read deadline: %v", d.md.keepAlive*2)
return nil
})
go d.keepAlive(cc)
}
return cc, nil
}
func (d *wsDialer) keepAlive(conn ws_util.WebsocketConn) {
ticker := time.NewTicker(d.md.keepAlive)
defer ticker.Stop()
for range ticker.C {
conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
if err := conn.WriteMessage(websocket.PingMessage, nil); err != nil {
return
}
d.options.Logger.Infof("send ping")
}
}
+5 -2
View File
@@ -21,7 +21,8 @@ type metadata struct {
writeBufferSize int
enableCompression bool
header http.Header
header http.Header
keepAlive time.Duration
}
func (d *wsDialer) parseMetadata(md mdata.Metadata) (err error) {
@@ -35,7 +36,8 @@ func (d *wsDialer) parseMetadata(md mdata.Metadata) (err error) {
writeBufferSize = "writeBufferSize"
enableCompression = "enableCompression"
header = "header"
header = "header"
keepAlive = "keepAlive"
)
d.md.host = mdata.GetString(md, host)
@@ -58,6 +60,7 @@ func (d *wsDialer) parseMetadata(md mdata.Metadata) (err error) {
}
d.md.header = h
}
d.md.keepAlive = mdata.GetDuration(md, keepAlive)
return
}
+42 -50
View File
@@ -10,7 +10,6 @@ import (
"github.com/go-gost/gost/pkg/dialer"
ws_util "github.com/go-gost/gost/pkg/internal/util/ws"
"github.com/go-gost/gost/pkg/logger"
md "github.com/go-gost/gost/pkg/metadata"
"github.com/go-gost/gost/pkg/registry"
"github.com/gorilla/websocket"
@@ -18,15 +17,14 @@ import (
)
func init() {
registry.RegisterDialer("mws", NewDialer)
registry.RegisterDialer("mwss", NewTLSDialer)
registry.DialerRegistry().Register("mws", NewDialer)
registry.DialerRegistry().Register("mwss", NewTLSDialer)
}
type mwsDialer struct {
sessions map[string]*muxSession
sessionMutex sync.Mutex
tlsEnabled bool
logger logger.Logger
md metadata
options dialer.Options
}
@@ -39,7 +37,6 @@ func NewDialer(opts ...dialer.Option) dialer.Dialer {
return &mwsDialer{
sessions: make(map[string]*muxSession),
logger: options.Logger,
options: options,
}
}
@@ -53,7 +50,6 @@ func NewTLSDialer(opts ...dialer.Option) dialer.Dialer {
return &mwsDialer{
tlsEnabled: true,
sessions: make(map[string]*muxSession),
logger: options.Logger,
options: options,
}
}
@@ -71,11 +67,6 @@ func (d *mwsDialer) Multiplex() bool {
}
func (d *mwsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOption) (conn net.Conn, err error) {
var options dialer.DialOptions
for _, opt := range opts {
opt(&options)
}
d.sessionMutex.Lock()
defer d.sessionMutex.Unlock()
@@ -85,7 +76,12 @@ func (d *mwsDialer) Dial(ctx context.Context, addr string, opts ...dialer.DialOp
ok = false
}
if !ok {
conn, err = d.dial(ctx, "tcp", addr, &options)
var options dialer.DialOptions
for _, opt := range opts {
opt(&options)
}
conn, err = options.NetDialer.Dial(ctx, "tcp", addr)
if err != nil {
return
}
@@ -107,11 +103,6 @@ func (d *mwsDialer) Handshake(ctx context.Context, conn net.Conn, options ...dia
d.sessionMutex.Lock()
defer d.sessionMutex.Unlock()
if d.md.handshakeTimeout > 0 {
conn.SetDeadline(time.Now().Add(d.md.handshakeTimeout))
defer conn.SetDeadline(time.Time{})
}
session, ok := d.sessions[opts.Addr]
if session != nil && session.conn != conn {
conn.Close()
@@ -125,7 +116,7 @@ func (d *mwsDialer) Handshake(ctx context.Context, conn net.Conn, options ...dia
}
s, err := d.initSession(ctx, host, conn)
if err != nil {
d.logger.Error(err)
d.options.Logger.Error(err)
conn.Close()
delete(d.sessions, opts.Addr)
return nil, err
@@ -143,34 +134,6 @@ func (d *mwsDialer) Handshake(ctx context.Context, conn net.Conn, options ...dia
return cc, nil
}
func (d *mwsDialer) dial(ctx context.Context, network, addr string, opts *dialer.DialOptions) (net.Conn, error) {
dial := opts.DialFunc
if dial != nil {
conn, err := dial(ctx, addr)
if err != nil {
d.logger.Error(err)
} else {
d.logger.WithFields(map[string]interface{}{
"src": conn.LocalAddr().String(),
"dst": addr,
}).Debug("dial with dial func")
}
return conn, err
}
var netd net.Dialer
conn, err := netd.DialContext(ctx, network, addr)
if err != nil {
d.logger.Error(err)
} else {
d.logger.WithFields(map[string]interface{}{
"src": conn.LocalAddr().String(),
"dst": addr,
}).Debugf("dial direct %s/%s", addr, network)
}
return conn, err
}
func (d *mwsDialer) initSession(ctx context.Context, host string, conn net.Conn) (*muxSession, error) {
dialer := websocket.Dialer{
HandshakeTimeout: d.md.handshakeTimeout,
@@ -188,13 +151,30 @@ func (d *mwsDialer) initSession(ctx context.Context, host string, conn net.Conn)
dialer.TLSClientConfig = d.options.TLSConfig
}
c, resp, err := dialer.Dial(url.String(), d.md.header)
if d.md.handshakeTimeout > 0 {
conn.SetReadDeadline(time.Now().Add(d.md.handshakeTimeout))
}
c, resp, err := dialer.DialContext(ctx, url.String(), d.md.header)
if err != nil {
return nil, err
}
resp.Body.Close()
conn = ws_util.Conn(c)
if d.md.handshakeTimeout > 0 {
conn.SetReadDeadline(time.Time{})
}
cc := ws_util.Conn(c)
if d.md.keepAlive > 0 {
c.SetReadDeadline(time.Now().Add(d.md.keepAlive * 2))
c.SetPongHandler(func(string) error {
c.SetReadDeadline(time.Now().Add(d.md.keepAlive * 2))
return nil
})
go d.keepAlive(cc)
}
// stream multiplex
smuxConfig := smux.DefaultConfig()
@@ -215,9 +195,21 @@ func (d *mwsDialer) initSession(ctx context.Context, host string, conn net.Conn)
smuxConfig.MaxStreamBuffer = d.md.muxMaxStreamBuffer
}
session, err := smux.Client(conn, smuxConfig)
session, err := smux.Client(cc, smuxConfig)
if err != nil {
return nil, err
}
return &muxSession{conn: conn, session: session}, nil
return &muxSession{conn: cc, session: session}, nil
}
func (d *mwsDialer) keepAlive(conn ws_util.WebsocketConn) {
ticker := time.NewTicker(d.md.keepAlive)
defer ticker.Stop()
for range ticker.C {
conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
if err := conn.WriteMessage(websocket.PingMessage, nil); err != nil {
return
}
}
}
+6 -2
View File
@@ -28,7 +28,8 @@ type metadata struct {
muxMaxReceiveBuffer int
muxMaxStreamBuffer int
header http.Header
header http.Header
keepAlive time.Duration
}
func (d *mwsDialer) parseMetadata(md mdata.Metadata) (err error) {
@@ -42,7 +43,8 @@ func (d *mwsDialer) parseMetadata(md mdata.Metadata) (err error) {
writeBufferSize = "writeBufferSize"
enableCompression = "enableCompression"
header = "header"
header = "header"
keepAlive = "keepAlive"
muxKeepAliveDisabled = "muxKeepAliveDisabled"
muxKeepAliveInterval = "muxKeepAliveInterval"
@@ -79,5 +81,7 @@ func (d *mwsDialer) parseMetadata(md mdata.Metadata) (err error) {
}
d.md.header = h
}
d.md.keepAlive = mdata.GetDuration(md, keepAlive)
return
}
+20 -18
View File
@@ -8,6 +8,7 @@ import (
"github.com/go-gost/gosocks4"
"github.com/go-gost/gosocks5"
netpkg "github.com/go-gost/gost/pkg/common/net"
"github.com/go-gost/gost/pkg/handler"
md "github.com/go-gost/gost/pkg/metadata"
"github.com/go-gost/gost/pkg/registry"
@@ -15,7 +16,7 @@ import (
)
func init() {
registry.RegisterHandler("auto", NewHandler)
registry.HandlerRegistry().Register("auto", NewHandler)
}
type autoHandler struct {
@@ -36,24 +37,24 @@ func NewHandler(opts ...handler.Option) handler.Handler {
options: options,
}
if f := registry.GetHandler("http"); f != nil {
if f := registry.HandlerRegistry().Get("http"); f != nil {
v := append(opts,
handler.LoggerOption(options.Logger.WithFields(map[string]interface{}{"type": "http"})))
handler.LoggerOption(options.Logger.WithFields(map[string]any{"type": "http"})))
h.httpHandler = f(v...)
}
if f := registry.GetHandler("socks4"); f != nil {
if f := registry.HandlerRegistry().Get("socks4"); f != nil {
v := append(opts,
handler.LoggerOption(options.Logger.WithFields(map[string]interface{}{"type": "socks4"})))
handler.LoggerOption(options.Logger.WithFields(map[string]any{"type": "socks4"})))
h.socks4Handler = f(v...)
}
if f := registry.GetHandler("socks5"); f != nil {
if f := registry.HandlerRegistry().Get("socks5"); f != nil {
v := append(opts,
handler.LoggerOption(options.Logger.WithFields(map[string]interface{}{"type": "socks5"})))
handler.LoggerOption(options.Logger.WithFields(map[string]any{"type": "socks5"})))
h.socks5Handler = f(v...)
}
if f := registry.GetHandler("relay"); f != nil {
if f := registry.HandlerRegistry().Get("relay"); f != nil {
v := append(opts,
handler.LoggerOption(options.Logger.WithFields(map[string]interface{}{"type": "relay"})))
handler.LoggerOption(options.Logger.WithFields(map[string]any{"type": "relay"})))
h.relayHandler = f(v...)
}
@@ -85,8 +86,8 @@ func (h *autoHandler) Init(md md.Metadata) error {
return nil
}
func (h *autoHandler) Handle(ctx context.Context, conn net.Conn) {
log := h.options.Logger.WithFields(map[string]interface{}{
func (h *autoHandler) Handle(ctx context.Context, conn net.Conn) error {
log := h.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
})
@@ -94,7 +95,7 @@ func (h *autoHandler) Handle(ctx context.Context, conn net.Conn) {
start := time.Now()
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
defer func() {
log.WithFields(map[string]interface{}{
log.WithFields(map[string]any{
"duration": time.Since(start),
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
}()
@@ -104,26 +105,27 @@ func (h *autoHandler) Handle(ctx context.Context, conn net.Conn) {
if err != nil {
log.Error(err)
conn.Close()
return
return err
}
conn = handler.NewBufferReaderConn(conn, br)
conn = netpkg.NewBufferReaderConn(conn, br)
switch b[0] {
case gosocks4.Ver4: // socks4
if h.socks4Handler != nil {
h.socks4Handler.Handle(ctx, conn)
return h.socks4Handler.Handle(ctx, conn)
}
case gosocks5.Ver5: // socks5
if h.socks5Handler != nil {
h.socks5Handler.Handle(ctx, conn)
return h.socks5Handler.Handle(ctx, conn)
}
case relay.Version1: // relay
if h.relayHandler != nil {
h.relayHandler.Handle(ctx, conn)
return h.relayHandler.Handle(ctx, conn)
}
default: // http
if h.httpHandler != nil {
h.httpHandler.Handle(ctx, conn)
return h.httpHandler.Handle(ctx, conn)
}
}
return nil
}
+18 -15
View File
@@ -13,6 +13,7 @@ import (
"github.com/go-gost/gost/pkg/chain"
"github.com/go-gost/gost/pkg/common/bufpool"
"github.com/go-gost/gost/pkg/handler"
"github.com/go-gost/gost/pkg/hosts"
resolver_util "github.com/go-gost/gost/pkg/internal/util/resolver"
"github.com/go-gost/gost/pkg/logger"
md "github.com/go-gost/gost/pkg/metadata"
@@ -26,13 +27,14 @@ const (
)
func init() {
registry.RegisterHandler("dns", NewHandler)
registry.HandlerRegistry().Register("dns", NewHandler)
}
type dnsHandler struct {
exchangers []exchanger.Exchanger
cache *resolver_util.Cache
router *chain.Router
hosts hosts.HostMapper
md metadata
options handler.Options
}
@@ -55,13 +57,12 @@ func (h *dnsHandler) Init(md md.Metadata) (err error) {
log := h.options.Logger
h.cache = resolver_util.NewCache().WithLogger(log)
h.router = &chain.Router{
Retries: h.options.Retries,
Chain: h.options.Chain,
Resolver: h.options.Resolver,
// Hosts: h.options.Hosts,
Logger: log,
h.router = h.options.Router
if h.router == nil {
h.router = (&chain.Router{}).WithLogger(log)
}
h.hosts = h.router.Hosts()
for _, server := range h.md.dns {
server = strings.TrimSpace(server)
@@ -97,18 +98,18 @@ func (h *dnsHandler) Init(md md.Metadata) (err error) {
return
}
func (h *dnsHandler) Handle(ctx context.Context, conn net.Conn) {
func (h *dnsHandler) Handle(ctx context.Context, conn net.Conn) error {
defer conn.Close()
start := time.Now()
log := h.options.Logger.WithFields(map[string]interface{}{
log := h.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
})
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
defer func() {
log.WithFields(map[string]interface{}{
log.WithFields(map[string]any{
"duration": time.Since(start),
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
}()
@@ -119,18 +120,20 @@ func (h *dnsHandler) Handle(ctx context.Context, conn net.Conn) {
n, err := conn.Read(*b)
if err != nil {
log.Error(err)
return
return err
}
reply, err := h.exchange(ctx, (*b)[:n], log)
if err != nil {
return
return err
}
defer bufpool.Put(&reply)
if _, err = conn.Write(reply); err != nil {
log.Error(err)
return err
}
return nil
}
func (h *dnsHandler) exchange(ctx context.Context, msg []byte, log logger.Logger) ([]byte, error) {
@@ -218,7 +221,7 @@ func (h *dnsHandler) exchange(ctx context.Context, msg []byte, log logger.Logger
// lookup host mapper
func (h *dnsHandler) lookupHosts(r *dns.Msg, log logger.Logger) (m *dns.Msg) {
if h.options.Hosts == nil ||
if h.hosts == nil ||
r.Question[0].Qclass != dns.ClassINET ||
(r.Question[0].Qtype != dns.TypeA && r.Question[0].Qtype != dns.TypeAAAA) {
return nil
@@ -231,7 +234,7 @@ func (h *dnsHandler) lookupHosts(r *dns.Msg, log logger.Logger) (m *dns.Msg) {
switch r.Question[0].Qtype {
case dns.TypeA:
ips, _ := h.options.Hosts.Lookup("ip4", host)
ips, _ := h.hosts.Lookup("ip4", host)
if len(ips) == 0 {
return nil
}
@@ -247,7 +250,7 @@ func (h *dnsHandler) lookupHosts(r *dns.Msg, log logger.Logger) (m *dns.Msg) {
}
case dns.TypeAAAA:
ips, _ := h.options.Hosts.Lookup("ip6", host)
ips, _ := h.hosts.Lookup("ip6", host)
if len(ips) == 0 {
return nil
}
+20 -18
View File
@@ -2,20 +2,22 @@ package local
import (
"context"
"errors"
"fmt"
"net"
"time"
"github.com/go-gost/gost/pkg/chain"
netpkg "github.com/go-gost/gost/pkg/common/net"
"github.com/go-gost/gost/pkg/handler"
md "github.com/go-gost/gost/pkg/metadata"
"github.com/go-gost/gost/pkg/registry"
)
func init() {
registry.RegisterHandler("tcp", NewHandler)
registry.RegisterHandler("udp", NewHandler)
registry.RegisterHandler("forward", NewHandler)
registry.HandlerRegistry().Register("tcp", NewHandler)
registry.HandlerRegistry().Register("udp", NewHandler)
registry.HandlerRegistry().Register("forward", NewHandler)
}
type forwardHandler struct {
@@ -46,12 +48,9 @@ func (h *forwardHandler) Init(md md.Metadata) (err error) {
h.group = chain.NewNodeGroup(&chain.Node{Name: "dummy", Addr: ":0"})
}
h.router = &chain.Router{
Retries: h.options.Retries,
Chain: h.options.Chain,
Resolver: h.options.Resolver,
Hosts: h.options.Hosts,
Logger: h.options.Logger,
h.router = h.options.Router
if h.router == nil {
h.router = (&chain.Router{}).WithLogger(h.options.Logger)
}
return
@@ -62,26 +61,27 @@ func (h *forwardHandler) Forward(group *chain.NodeGroup) {
h.group = group
}
func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn) {
func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn) error {
defer conn.Close()
start := time.Now()
log := h.options.Logger.WithFields(map[string]interface{}{
log := h.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
})
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
defer func() {
log.WithFields(map[string]interface{}{
log.WithFields(map[string]any{
"duration": time.Since(start),
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
}()
target := h.group.Next()
if target == nil {
log.Error("no target available")
return
err := errors.New("target not available")
log.Error(err)
return err
}
network := "tcp"
@@ -89,7 +89,7 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn) {
network = "udp"
}
log = log.WithFields(map[string]interface{}{
log = log.WithFields(map[string]any{
"dst": fmt.Sprintf("%s/%s", target.Addr, network),
})
@@ -101,15 +101,17 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn) {
// TODO: the router itself may be failed due to the failed node in the router,
// the dead marker may be a wrong operation.
target.Marker.Mark()
return
return err
}
defer cc.Close()
target.Marker.Reset()
t := time.Now()
log.Infof("%s <-> %s", conn.RemoteAddr(), target.Addr)
handler.Transport(conn, cc)
log.WithFields(map[string]interface{}{
netpkg.Transport(conn, cc)
log.WithFields(map[string]any{
"duration": time.Since(t),
}).Infof("%s >-< %s", conn.RemoteAddr(), target.Addr)
return nil
}
+19 -17
View File
@@ -2,19 +2,21 @@ package remote
import (
"context"
"errors"
"fmt"
"net"
"time"
"github.com/go-gost/gost/pkg/chain"
netpkg "github.com/go-gost/gost/pkg/common/net"
"github.com/go-gost/gost/pkg/handler"
md "github.com/go-gost/gost/pkg/metadata"
"github.com/go-gost/gost/pkg/registry"
)
func init() {
registry.RegisterHandler("rtcp", NewHandler)
registry.RegisterHandler("rudp", NewHandler)
registry.HandlerRegistry().Register("rtcp", NewHandler)
registry.HandlerRegistry().Register("rudp", NewHandler)
}
type forwardHandler struct {
@@ -40,12 +42,9 @@ func (h *forwardHandler) Init(md md.Metadata) (err error) {
return
}
h.router = &chain.Router{
Retries: h.options.Retries,
Chain: h.options.Chain,
Resolver: h.options.Resolver,
Hosts: h.options.Hosts,
Logger: h.options.Logger,
h.router = h.options.Router
if h.router == nil {
h.router = (&chain.Router{}).WithLogger(h.options.Logger)
}
return
@@ -56,26 +55,27 @@ func (h *forwardHandler) Forward(group *chain.NodeGroup) {
h.group = group
}
func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn) {
func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn) error {
defer conn.Close()
start := time.Now()
log := h.options.Logger.WithFields(map[string]interface{}{
log := h.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
})
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
defer func() {
log.WithFields(map[string]interface{}{
log.WithFields(map[string]any{
"duration": time.Since(start),
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
}()
target := h.group.Next()
if target == nil {
log.Error("no target available")
return
err := errors.New("target not available")
log.Error(err)
return err
}
network := "tcp"
@@ -83,7 +83,7 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn) {
network = "udp"
}
log = log.WithFields(map[string]interface{}{
log = log.WithFields(map[string]any{
"dst": fmt.Sprintf("%s/%s", target.Addr, network),
})
@@ -95,15 +95,17 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn) {
// TODO: the router itself may be failed due to the failed node in the router,
// the dead marker may be a wrong operation.
target.Marker.Mark()
return
return err
}
defer cc.Close()
target.Marker.Reset()
t := time.Now()
log.Infof("%s <-> %s", conn.RemoteAddr(), target.Addr)
handler.Transport(conn, cc)
log.WithFields(map[string]interface{}{
netpkg.Transport(conn, cc)
log.WithFields(map[string]any{
"duration": time.Since(t),
}).Infof("%s >-< %s", conn.RemoteAddr(), target.Addr)
return nil
}
+1 -1
View File
@@ -10,7 +10,7 @@ import (
type Handler interface {
Init(metadata.Metadata) error
Handle(context.Context, net.Conn)
Handle(context.Context, net.Conn) error
}
type Forwarder interface {
+28 -40
View File
@@ -16,9 +16,8 @@ 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"
netpkg "github.com/go-gost/gost/pkg/common/net"
"github.com/go-gost/gost/pkg/handler"
"github.com/go-gost/gost/pkg/logger"
md "github.com/go-gost/gost/pkg/metadata"
@@ -26,14 +25,13 @@ import (
)
func init() {
registry.RegisterHandler("http", NewHandler)
registry.HandlerRegistry().Register("http", NewHandler)
}
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,29 +50,25 @@ 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,
Resolver: h.options.Resolver,
Hosts: h.options.Hosts,
Logger: h.options.Logger,
h.router = h.options.Router
if h.router == nil {
h.router = (&chain.Router{}).WithLogger(h.options.Logger)
}
return nil
}
func (h *httpHandler) Handle(ctx context.Context, conn net.Conn) {
func (h *httpHandler) Handle(ctx context.Context, conn net.Conn) error {
defer conn.Close()
start := time.Now()
log := h.options.Logger.WithFields(map[string]interface{}{
log := h.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
})
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
defer func() {
log.WithFields(map[string]interface{}{
log.WithFields(map[string]any{
"duration": time.Since(start),
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
}()
@@ -82,18 +76,14 @@ func (h *httpHandler) Handle(ctx context.Context, conn net.Conn) {
req, err := http.ReadRequest(bufio.NewReader(conn))
if err != nil {
log.Error(err)
return
return err
}
defer req.Body.Close()
h.handleRequest(ctx, conn, req, log)
return h.handleRequest(ctx, conn, req, log)
}
func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *http.Request, log logger.Logger) {
if req == nil {
return
}
func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *http.Request, log logger.Logger) error {
if h.md.sni && !req.URL.IsAbs() && govalidator.IsDNSName(req.Host) {
req.URL.Scheme = "http"
}
@@ -124,7 +114,7 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
addr = net.JoinHostPort(addr, "80")
}
fields := map[string]interface{}{
fields := map[string]any{
"dst": addr,
}
if u, _, _ := h.basicProxyAuth(req.Header.Get("Proxy-Authorization"), log); u != "" {
@@ -156,30 +146,27 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
}
log.Info("bypass: ", addr)
resp.Write(conn)
return
return resp.Write(conn)
}
if !h.authenticate(conn, req, resp, log) {
return
return nil
}
if network == "udp" {
h.handleUDP(ctx, conn, network, req.Host, log)
return
return h.handleUDP(ctx, conn, network, req.Host, log)
}
if req.Method == "PRI" ||
(req.Method != http.MethodConnect && req.URL.Scheme != "http") {
resp.StatusCode = http.StatusBadRequest
resp.Write(conn)
if log.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpResponse(resp, false)
log.Debug(string(dump))
}
return
return resp.Write(conn)
}
req.Header.Del("Proxy-Authorization")
@@ -187,13 +174,12 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
cc, err := h.router.Dial(ctx, network, addr)
if err != nil {
resp.StatusCode = http.StatusServiceUnavailable
resp.Write(conn)
if log.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpResponse(resp, false)
log.Debug(string(dump))
}
return
return resp.Write(conn)
}
defer cc.Close()
@@ -207,22 +193,24 @@ func (h *httpHandler) handleRequest(ctx context.Context, conn net.Conn, req *htt
}
if err = resp.Write(conn); err != nil {
log.Error(err)
return
return err
}
} else {
req.Header.Del("Proxy-Connection")
if err = req.Write(cc); err != nil {
log.Error(err)
return
return err
}
}
start := time.Now()
log.Infof("%s <-> %s", conn.RemoteAddr(), addr)
handler.Transport(conn, cc)
log.WithFields(map[string]interface{}{
netpkg.Transport(conn, cc)
log.WithFields(map[string]any{
"duration": time.Since(start),
}).Infof("%s >-< %s", conn.RemoteAddr(), addr)
return nil
}
func (h *httpHandler) decodeServerName(s string) (string, error) {
@@ -266,7 +254,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
}
@@ -299,7 +287,7 @@ func (h *httpHandler) authenticate(conn net.Conn, req *http.Request, resp *http.
defer cc.Close()
req.Write(cc)
handler.Transport(conn, cc)
netpkg.Transport(conn, cc)
return
case "file":
f, _ := os.Open(pr.Value)
+16 -12
View File
@@ -2,18 +2,19 @@ package http
import (
"context"
"errors"
"net"
"net/http"
"net/http/httputil"
"time"
"github.com/go-gost/gost/pkg/common/net/relay"
"github.com/go-gost/gost/pkg/common/util/socks"
"github.com/go-gost/gost/pkg/handler"
"github.com/go-gost/gost/pkg/logger"
)
func (h *httpHandler) handleUDP(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) {
log = log.WithFields(map[string]interface{}{
func (h *httpHandler) handleUDP(ctx context.Context, conn net.Conn, network, address string, log logger.Logger) error {
log = log.WithFields(map[string]any{
"cmd": "udp",
})
@@ -28,15 +29,15 @@ func (h *httpHandler) handleUDP(ctx context.Context, conn net.Conn, network, add
if !h.md.enableUDP {
resp.StatusCode = http.StatusForbidden
resp.Write(conn)
if log.IsLevelEnabled(logger.DebugLevel) {
dump, _ := httputil.DumpResponse(resp, false)
log.Debug(string(dump))
}
log.Error("UDP relay is diabled")
return
log.Error("http: UDP relay is disabled")
return resp.Write(conn)
}
resp.StatusCode = http.StatusOK
@@ -46,31 +47,34 @@ func (h *httpHandler) handleUDP(ctx context.Context, conn net.Conn, network, add
}
if err := resp.Write(conn); err != nil {
log.Error(err)
return
return err
}
// obtain a udp connection
c, err := h.router.Dial(ctx, "udp", "") // UDP association
if err != nil {
log.Error(err)
return
return err
}
defer c.Close()
pc, ok := c.(net.PacketConn)
if !ok {
log.Errorf("wrong connection type")
return
err = errors.New("wrong connection type")
log.Error(err)
return err
}
relay := handler.NewUDPRelay(socks.UDPTunServerConn(conn), pc).
relay := relay.NewUDPRelay(socks.UDPTunServerConn(conn), pc).
WithBypass(h.options.Bypass).
WithLogger(log)
t := time.Now()
log.Infof("%s <-> %s", conn.RemoteAddr(), pc.LocalAddr())
relay.Run()
log.WithFields(map[string]interface{}{
log.WithFields(map[string]any{
"duration": time.Since(t),
}).Infof("%s >-< %s", conn.RemoteAddr(), pc.LocalAddr())
return nil
}
+32 -33
View File
@@ -18,9 +18,8 @@ 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"
netpkg "github.com/go-gost/gost/pkg/common/net"
"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"
@@ -29,14 +28,13 @@ import (
)
func init() {
registry.RegisterHandler("http2", NewHandler)
registry.HandlerRegistry().Register("http2", NewHandler)
}
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,44 +53,42 @@ 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,
Resolver: h.options.Resolver,
Hosts: h.options.Hosts,
Logger: h.options.Logger,
h.router = h.options.Router
if h.router == nil {
h.router = (&chain.Router{}).WithLogger(h.options.Logger)
}
return nil
}
func (h *http2Handler) Handle(ctx context.Context, conn net.Conn) {
func (h *http2Handler) Handle(ctx context.Context, conn net.Conn) error {
defer conn.Close()
start := time.Now()
log := h.options.Logger.WithFields(map[string]interface{}{
log := h.options.Logger.WithFields(map[string]any{
"remote": conn.RemoteAddr().String(),
"local": conn.LocalAddr().String(),
})
log.Infof("%s <> %s", conn.RemoteAddr(), conn.LocalAddr())
defer func() {
log.WithFields(map[string]interface{}{
log.WithFields(map[string]any{
"duration": time.Since(start),
}).Infof("%s >< %s", conn.RemoteAddr(), conn.LocalAddr())
}()
cc, ok := conn.(*http2_util.ServerConn)
if !ok {
log.Error("wrong connection type")
return
err := errors.New("wrong connection type")
log.Error(err)
return err
}
h.roundTrip(ctx, cc.Writer(), cc.Request(), log)
return h.roundTrip(ctx, cc.Writer(), cc.Request(), log)
}
// NOTE: there is an issue (golang/go#43989) will cause the client hangs
// when server returns an non-200 status code,
// May be fixed in go1.18.
func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req *http.Request, log logger.Logger) {
func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req *http.Request, log logger.Logger) error {
// Try to get the actual host.
// Compatible with GOST 2.x.
if v := req.Header.Get("Gost-Target"); v != "" {
@@ -114,7 +110,7 @@ func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
addr = net.JoinHostPort(addr, "80")
}
fields := map[string]interface{}{
fields := map[string]any{
"dst": addr,
}
if u, _, _ := h.basicProxyAuth(req.Header.Get("Proxy-Authorization")); u != "" {
@@ -135,7 +131,7 @@ func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
if h.options.Bypass != nil && h.options.Bypass.Contains(addr) {
w.WriteHeader(http.StatusForbidden)
log.Info("bypass: ", addr)
return
return nil
}
resp := &http.Response{
@@ -146,7 +142,7 @@ func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
}
if !h.authenticate(w, req, resp, log) {
return
return nil
}
// delete the proxy related headers.
@@ -157,7 +153,7 @@ func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
if err != nil {
log.Error(err)
w.WriteHeader(http.StatusServiceUnavailable)
return
return err
}
defer cc.Close()
@@ -174,28 +170,31 @@ func (h *http2Handler) roundTrip(ctx context.Context, w http.ResponseWriter, req
if err != nil {
log.Error(err)
w.WriteHeader(http.StatusInternalServerError)
return
return err
}
defer conn.Close()
start := time.Now()
log.Infof("%s <-> %s", conn.RemoteAddr(), addr)
handler.Transport(conn, cc)
log.WithFields(map[string]interface{}{
netpkg.Transport(conn, cc)
log.WithFields(map[string]any{
"duration": time.Since(start),
}).Infof("%s >-< %s", conn.RemoteAddr(), addr)
return
return nil
}
start := time.Now()
log.Infof("%s <-> %s", req.RemoteAddr, addr)
handler.Transport(&readWriter{r: req.Body, w: flushWriter{w}}, cc)
log.WithFields(map[string]interface{}{
netpkg.Transport(&readWriter{r: req.Body, w: flushWriter{w}}, cc)
log.WithFields(map[string]any{
"duration": time.Since(start),
}).Infof("%s >-< %s", req.RemoteAddr, addr)
return
return nil
}
// TODO: forward request
return nil
}
func (h *http2Handler) decodeServerName(s string) (string, error) {
@@ -239,7 +238,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
}

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